Russian Qt Forum
Декабрь 20, 2024, 01:44 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Урок: Как передавать через буфер обмена произвольные типы данных  (Прочитано 23208 раз)
xintrea
Moderator
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« : Декабрь 04, 2008, 19:55 »

Как передавать через буфер обмена произвольные типы данных

Рано или поздно, при разработке более-менее крупного проекта, возникает задача передачи через буфер обмена самодельных данных. В литературе и документации по 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.
« Последнее редактирование: Декабрь 05, 2008, 12:35 от pastor » Записан

Собираю информацию по крупицам
http://webhamster.ru
panAlexey
Гипер активный житель
*****
Offline Offline

Сообщений: 864

Акцио ЗАРПЛАТА!!!!! :(


Просмотр профиля
« Ответ #1 : Сентябрь 26, 2009, 19:14 »

Что насчет копирования структуры это между приложениями, в которых есть её декларация?
Сейчас сижу и думаю, каким путем пойти. Структуры у мну сложные...
Записан

Win Xp SP-2, Qt4.3.4/MinGW. http://trdm.1gb.ru/
xintrea
Moderator
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #2 : Октябрь 02, 2009, 00:18 »

По идее, должно передаваться между двумя приложениями, если (в нашем примере) структура clipb_struct будет одинаковой в обоих приложениях.
Записан

Собираю информацию по крупицам
http://webhamster.ru
panAlexey
Гипер активный житель
*****
Offline Offline

Сообщений: 864

Акцио ЗАРПЛАТА!!!!! :(


Просмотр профиля
« Ответ #3 : Октябрь 04, 2009, 15:32 »

Ну, доделай пример так, что-бы приложение при старте проверяло буфер обмена и если там структура, то цепляло её.
Записан

Win Xp SP-2, Qt4.3.4/MinGW. http://trdm.1gb.ru/
jurtal
Гость
« Ответ #4 : Март 28, 2012, 16:52 »

Сделал как описано выше под виндой и Qt4.7.1.  При копировании объекта clipb между приложениями получаю нулевой указатель cb. В пределах одного приложения - все нормально. В чем ошибка?

void Widget::onPaste()
{
    const QClipboard *clipboard = QApplication::clipboard();
    const QMimeData *mimeData = clipboard->mimeData();
    if (! mimeData->hasFormat("myapp/multidata"))
   {
       return;
    }

    const clipb *cb =qobject_cast<const clipb *>(mimeData);
}   
Записан
jurtal
Гость
« Ответ #5 : Март 29, 2012, 10:34 »

Попробовал приаттаченный пример - тоже не работает. Видимо урок только в пределах одного приложения справедлив...
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #6 : Март 29, 2012, 13:06 »

исходники не качал, но в примере кода в первом посте нету задания миме-типа для объекта типа clipb - может поэтому?
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Hammer
Гость
« Ответ #7 : Апрель 12, 2013, 15:17 »

А как насчет передачи массива из таких структур, например? В моем случае нужно чтобы копипастились графические элементы и не по одному, а кучно.

Хотя, нафига мне буфер? ) Я ж и сам могу
« Последнее редактирование: Апрель 12, 2013, 16:24 от Hammer » Записан
xintrea
Moderator
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #8 : Апрель 15, 2013, 20:52 »

исходники не качал, но в примере кода в первом посте нету задания миме-типа для объекта типа clipb - может поэтому?

Ну как же не задается:

Код:
clipb_formats << "myapp/multidata";

...

// Перегруженный метод QMimeData
QStringList clipb::formats() const
{
 return clipb_formats;
}

Так что дело наверно в другом. Я сейчас несколько отошел от Qt, так что помочь не могу.
Записан

Собираю информацию по крупицам
http://webhamster.ru
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.302 секунд. Запросов: 23.