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

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

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

Сообщений: 754



Просмотр профиля WWW
« Ответ #15 : Ноябрь 13, 2008, 19:00 »

Надо чтоб в результатох поиска по форуму отображались и результаты поиска в вики. Это админ может сделать? Без такого поиска никуда не уедем.
Записан

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

Сообщений: 754



Просмотр профиля WWW
« Ответ #16 : Ноябрь 16, 2008, 02:20 »

А вот и обещаный урок.

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

Рано или поздно при разработке более-менее крупного проекта, возникает задача передачи через буфер обмена самодельных данных. В литературе и документации по 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()

Код:
QVariant retrieveData ( const QString & mimeType, QVariant::Type type ) const


Если бы мы возвращали данные в виде QByteArray, то особых проблем бы не возникло - QByteArray прекрасно передается через QVariant. Но тогда нужно будет городить низкоуровневую упаковку и разбор данных из такого массива. А это ничем не отличается от работы с буфером обмена через метод setData().

Поэтому, первая наша задача - разобраться, как передавать через QVariant самодельную структуру данных. Как только мы это поймем, сделать все остальное не составит большого труда.

Не буду отсылать к документации, и объясню на пальцах, как передавать произвольные структуры чере QVariant. Предположим, у нас есть некая структура

Код:
// Структура данных, которая будет передаваться через буфер обмена
struct clipb_struct
{
 int number; // Некое число
 QString stringline; // Некая строка
 QMap<QString, QString> namevaluetable; // Некая таблица вида "имя переменной" "значение"
};

Чтобы передавать через QVariant данные с типом clipb_struct, надо этот тип просто-напросто зарегистрировать в программе. Делается это так

Код:
Q_DECLARE_METATYPE(clipb_struct);

само собой, данный код нужно размещать после описания структуры. После этого действия, структура clipb_struct будет передаваться через QVariant.


Ну а дальше все просто. Пишется класс, унаследованный от QMimeData. В классе делаем переменную (свойство) типа clipb_struct. Пишем методы (или метод), которые будут заполнять поля данной переменной. Переопределяем метод retrieveData(), он как раз и должен возвращать переменную типа clipb_struct. Можем так же написать методы-хелперы, которые будут возвращать значения полей переменной типа clipb_struct.

Работу с буфером обмена можно организовать так.

Внесение информации в буфер обмена

Код:
 // Создается ссылка на буфер обмена
 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 );

Извлечение данных из буфера обмена

Код:
 // Создается ссылка на буфер обмена
 QClipboard *getbuf=QApplication::clipboard();

 // Создается объект для приема данных
 const clipb *cb2;

 // Данные из буфера вставляются в принимающий объект
 cb2=qobject_cast<const clipb *>(getbuf->mimeData());

после таких действий в объекте cb2 в переменной типа clipb_struct будут лежать переданные через буфер данные. И их можно извлекать далее с помощью методов-хелперов, или с помощью одного метода, который просто возращает переменную типа clipb_struct.


Чтобы все стало более понятно,  прикрепляю готовый пример. В примере демонстрируется передача соственного типа данных через буфер обмена. Пример предельно простой, нет никаких кнопочек, только главное окно программы. В примере происходит копирование в буфер, и, сразу, извлечение из буфера. Вся последовательность действий пишется в консоль.

Проект с исходниками дан в нескольких кодировках.
« Последнее редактирование: Ноябрь 16, 2008, 23:37 от xintrea » Записан

Собираю информацию по крупицам
http://webhamster.ru
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #17 : Ноябрь 16, 2008, 09:59 »

Вики - унылое Г потому, что занимаюсь только я, а времени не очень много. Предполагалось, что я начну, а люди подтянутся. Вот и подтягивайтесь. Можете мне статьи давать, а я буду их в вику ложить, если не хотите на регистрацию заморачиваться.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
BRE
Гость
« Ответ #18 : Ноябрь 16, 2008, 10:12 »

Извлечение данных из буфера обмена

Код:
 // Создается ссылка на буфер обмена
 QClipboard *getbuf=QApplication::clipboard();

 // Создается объект для приема данных
 const clipb *cb2=new clipb();

 // Данные из буфера вставляются в принимающий объект
 cb2=qobject_cast<const clipb *>(getbuf->mimeData());


Зачем создавать объект для приема данных, если следом указателю присваивается новое значение?
Может лучше так:
Код:
// Создается ссылка на буфер обмена
QClipboard *getbuf=QApplication::clipboard();

const clipb *cb2=qobject_cast<const clipb *>(getbuf->mimeData());
cb2->print();
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #19 : Ноябрь 16, 2008, 22:52 »

Зачем создавать объект для приема данных, если следом указателю присваивается новое значение?
Может лучше так:
Код:
// Создается ссылка на буфер обмена
QClipboard *getbuf=QApplication::clipboard();

const clipb *cb2=qobject_cast<const clipb *>(getbuf->mimeData());
cb2->print();

Чтобы понять зачем, стоит прочитать книжку "Совершенный код". Кратко говоря, логически атомарные операции для улучшения понятности кода человеком, надо делать отдельно, и на валить все в одну кучу.

Если уж вы стали "оптимизировать", то давайте будем последовательны и не станем останавливаться на инициализации значением при создании переменной. Давайте тогда уж писать так

Код:
const clipb *cb2=qobject_cast<const clipb *>(QApplication::clipboard()->mimeData());

ведь нам и ссылка на буфер обмена не нужна. А еще в каждом классе можно писать реализации простых методов прямо в заголовке класса. Давайте тоже так делать. Преставляю такой замечательный урок и радость новичка при прочтении.
« Последнее редактирование: Ноябрь 16, 2008, 23:03 от xintrea » Записан

Собираю информацию по крупицам
http://webhamster.ru
Rcus
Гость
« Ответ #20 : Ноябрь 16, 2008, 23:05 »

Но все же на вопрос вы не ответили.
Выделив память в куче вы должны точно знать кто ее освободит и когда, в вашем же примере вызов new clipbrd() гарантированно приведет к утечке памяти.
И кстати однострочный вариант выглядит экспрессивнее Улыбающийся (именно за цепочки вызовов я люблю C++)
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #21 : Ноябрь 16, 2008, 23:12 »

Вики - унылое Г потому, что занимаюсь только я, а времени не очень много. Предполагалось, что я начну, а люди подтянутся. Вот и подтягивайтесь.

Подтянусь только в том случае, если поиск по данному форуму будет индексировать и статьи в вики. А в результатах поиска будут, соответсвенно, видны и и обычные ссылки на темы форума, и ссылки на вики-статьи. Заметьте, не "ссылка на сообщение с сылкой на вики-статью", а прямые ссылки на вики-статью. Лишний клик - меньше знаний с экспоненциальной зависимостью.

Если поиска по вики в пространстве форума это не будет, толку от вики никакого.
Записан

Собираю информацию по крупицам
http://webhamster.ru
BRE
Гость
« Ответ #22 : Ноябрь 16, 2008, 23:13 »

Чтобы понять зачем, стоит прочитать книжку "Совершенный код".
Почитай лучше книги ты, посмотри свой код.  Подмигивающий
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #23 : Ноябрь 16, 2008, 23:19 »

Но все же на вопрос вы не ответили.
Выделив память в куче вы должны точно знать кто ее освободит и когда, в вашем же примере вызов new clipbrd() гарантированно приведет к утечке памяти.
И кстати однострочный вариант выглядит экспрессивнее Улыбающийся (именно за цепочки вызовов я люблю C++)

Объясняю. Дело в том, что здесь идет работа с буфером обмена. Который может быть заполнен независимо от нас. Поэтому нам лучше сделать копию данных и дальше спокойно работать с копией, а не с сылкой на объект в буфере. Конечно, в таком простом примере с буфером ничего плохого не произойдет. В реальных условиях возможны варианты, особенно если обработка буфера производится в смежных основных циклах Qt программы.

Хотя да, лоханулся, копирования данных у меня не происходит. И действия хоть и работают, но бессмысленно new. Поэтому я и нелюблю C/C++, но приходится прогать на нем, ибо альтернатив нет.
« Последнее редактирование: Ноябрь 16, 2008, 23:22 от xintrea » Записан

Собираю информацию по крупицам
http://webhamster.ru
Rcus
Гость
« Ответ #24 : Ноябрь 16, 2008, 23:22 »

Все понятно, возможно именно таким было ваше намерение, но все же ваш код не копирует объект (и умоляю вас не упоминайте CC всуе).
Записан
BRE
Гость
« Ответ #25 : Ноябрь 16, 2008, 23:24 »

Объясняю. Дело в том, что здесь идет работа с буфером обмена. Который может быть заполнен независимо от нас. Поэтому нам лучше сделать копию данных и дальше спокойно работать с копией, а не с сылкой на объект в буфере. Конечно, в таком простом примере с буфером ничего плохого не произойдет. В реальных условиях возможны варианты, особенно если обработка буфера производится в смежных основных циклах Qt программы.
Спасибо за объяснения.  Улыбающийся
Код:
 const clipb *cb2=new clipb();
 cb2=qobject_cast<const clipb *>(getbuf->mimeData());
В первой строке ты выделяешь память, на которую ссылается указатель cb2, во второй строке ты теряешь ту память на которую указывал этот указатель, и она остается не освобожденной.
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #26 : Ноябрь 16, 2008, 23:38 »

Все понятно, возможно именно таким было ваше намерение, но все же ваш код не копирует объект (и умоляю вас не упоминайте CC всуе).

Исправил.

А что есть CC? Я не знаю такого.
Записан

Собираю информацию по крупицам
http://webhamster.ru
BRE
Гость
« Ответ #27 : Ноябрь 16, 2008, 23:44 »

Хотя да, лоханулся, копирования данных у меня не происходит. И действия хоть и работают, но бессмысленно new. Поэтому я и нелюблю C/C++, но приходится прогать на нем, ибо альтернатив нет.
1. new был не бессмысленен, а вреден. Ты терял бы память на каждом извлечении из clipa.
2. Нет такого языка С/С++. Это два абсолютно разных языка, с разной идеологией. А вот когда ты это прочувствуешь, тебе понравится и С и С++.  Подмигивающий
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #28 : Ноябрь 16, 2008, 23:54 »

2. Нет такого языка С/С++. Это два абсолютно разных языка, с разной идеологией.

А мужики-то не знают. Срочно бегу на форумы по JavaScript объяснять, что "JavaScript -это не Java".
Записан

Собираю информацию по крупицам
http://webhamster.ru
BRE
Гость
« Ответ #29 : Ноябрь 16, 2008, 23:58 »

2. Нет такого языка С/С++. Это два абсолютно разных языка, с разной идеологией.

А мужики-то не знают. Срочно бегу на форумы по JavaScript объяснять, что "JavaScript -это не Java".

Зря прикалываешься. С++ использует синтаксис С. Но на С++ нельзя писать как на С, иначе теряется его смысл. Это два языка с абсолютно разными подходами к программированию.
Лениво сейчас все расписывать. И я так понимаю, что С++ тебе не очень то интересен. Глянь эту ссылку http://www.insidecpp.ru/art/1/.
« Последнее редактирование: Ноябрь 17, 2008, 00:04 от BRE » Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


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