Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование для начинающих и не только


Информационный Канал Subscribe.Ru


"Межплатформенный" Drag & Drop (Часть 2. Linux KDE 3.1)

*

Те из вас, кто уже пробовал Kylix могут заметить, что переход на него действительно не вызывает сложностей, пока вы не выходите за рамки готовых компонент, но теперь мы покажем, как можно проделать такое "нестандартное" действие под Linux.

Определения

Konqueror Screenshotрис.1

Для начала, что б не было неясностей скажу, что испытания проводились на ASPLinux 7.3, которая поставлялась на диске "К+П" №2 - 2003г. программа была написана на Borland Kylix v1.0 Server Developer. При проверке приложения использовался Konqueror(рис.3) - стандартный проводник KDE.

Немного теории

Во первых - наше приложение будет основываться на работе с модулем Qt.pas, в котором объявлены все жизненно важные объекты, переменные, типы и т.д.1 Также заметим, что работа приложений под Linux (на мой взгляд) кардинально отличается от работы приложений в Windows. Так если Windows API основано на обработке сообщений, то в Linux их роль играют сигналы и слоты, а при использовании Qt - события(отдаленно напоминающие события компонентов Delphi). Взаимодействие с системой осуществляется не путем перехвата сообщений, а путем создания реакции на событие и его регистрации.

А теперь лезем в "дебри" Qt.

Сперва мы воспользуемся функцией QEvent_hook_create для создания экземпляра объекта, который бы реагировал на события:
function QEvent_hook_create(handle: QObjectH): QEvent_hookH; cdecl;

  • Handle
    - идентификатор объекта, для которого создается реакция на событие.
  • Результат
    - идентификатор реагирующего объекта.
По завершении работы приложения надо будет освободить реагирующий объект:
procedure QEvent_hook_destroy(handle: QEvent_hookH); cdecl; Теперь нам надо создать собственно реакцию на событие, которое должно иметь следующий вид:
TEventFilterMethod = function (Sender: QObjectH; Event: QEventH): Boolean of object cdecl;
    где:
  • Sender
    - идентификатор объекта который должен реагировать на событие.
  • Event
    - идентификатор события, на которое должен реагировать объект.
  • Результат
    - True если на событие успешно отреагировали, False - если нет. Т.к. мы пишем реакцию, которая должна незаметно влиять на работу нашего компонента, то результат всегда должен быть равен False;
После этого нам необходимо инициализировать ее:
procedure Qt_hook_hook_events(handle: QObject_hookH; hook: QHookH); cdecl;
    где:
  • Handle
    - идентификатор объекта-реакции на событие.
  • Hook
    - метод, который собственно и реагирует на события объекта.
В самом методе Hook нам необходимо разобрать, на какие события следует реагировать т.к. ему передаются все без исключения события связанные с прикрепленным (через QEvent_hook_create) объектом. Для выделения необходимого события используются методы:QEvent_isXXXXXX, где XXXX - название события. Для наших нужд потребуется только одни метод - QEvent_isQDropEventEvent:
function QEvent_isQDropEventEvent(e: QEventH): Boolean; cdecl;
    где:
  • e
    - значение переданное параметру Event из шаблона TEventFilterMethod
  • Результат
    - True если событие относится к Drag&Drop, False в противном случае.
После того, как QEvent_isQDropEventEvent вернул true нам следует перекодировать событие в QMimeSourceH посредством метода: function QDropEvent_to_QMimeSource(handle: QDropEventH): QMimeSourceH; cdecl; далее принять на обработку это событие: procedure QDropEvent_acceptAction(handle: QDropEventH; y: Boolean); cdecl; и наконец декодировать событие в приятный нашему взгляду вид: function QTextDrag_decode(e: QMimeSourceH; s: PWideString): Boolean; overload; cdecl; после этого мы можем спокойно обрабатывать наши "перетасканные" файлы.

А теперь к делу

Итак для начала создадим форму и разместим на ней кнопку и компонент TListBox, которые собственно и будет играть роль приемника перетаскиваний. В кнопке мы запишем код, который будет инициализировать наш TListBox на обработку событий Drag & Drop:


procedure TForm1.Button1Click(Sender: TObject);
begin
 QWidget_setAcceptDrops(ListBox1.Handle,True);
 Evt:=QEvent_hook_create(Self.ListBox1.Handle);
 //Filter:=Self.EvtFilter;
 TEventFilterMethod(H):=EvtFilter;
 Qt_hook_hook_events(Evt,H);
end;
при закрытии формы мы должны освободить наш объект Evt от исполняемых им обязанностей:

procedure TForm1.FormDestroy(Sender: TObject);
begin
 QEvent_hook_destroy(Evt);
end;
теперь нам осталось только написать необходимую реакцию на события:

function TForm1.EvtFilter(Sender:QObjectH; Event:QEventH):Boolean;
var QMS:QMimeSourceH;
    S:WideString;
begin
Result:=False;
if QEvent_isQDropEventEvent(Event) then
 begin
  QMS:=QDropEvent_to_QMimeSource(QDropEventH(Event));
  QDropEvent_acceptAction(QDropEventH(Event),QTextDrag_canDecode(QMS));
  if QTextDrag_canDecode(QMS) then
   begin
    ListBox1.Items.Clear;
    QTextDrag_decode(QMS,@S);
    ListBox1.Items.Add(S);
   end 
 end else
if QEvent_isQCloseEvent(Event) then
 QEvent_hook_destroy(Evt);
end;
Код:
 if QEvent_isQCloseEvent(Event) then
  QEvent_hook_destroy(Evt);
Добавлен просто так, на всякий случай. Время от Времени у меня Kylix зависал при закрытии приложения, а после добавления такой строчки кода - ни разу. В листинге 2 показан код приложения, о котором шла речь в этой статье.

//Листинг 2

unit Unit1; interface uses SysUtils, Types, Classes, Variants, QGraphics, QControls, QForms, QDialogs,Qt, QStdCtrls; type TForm1 = class(TForm) ListBox1: TListBox; Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormDestroy(Sender: TObject); private Evt:QEvent_hookH; H:TMethod; // FFilter:TEventFilterMethod; function EvtFilter(Sender: QObjectH; Event: QEventH): Boolean; cdecl; { Private declarations } public { Public declarations } // property Filter:TEventFilterMethod read FFilter write FFilter; end; var Form1: TForm1; implementation {$R *.xfm} function TForm1.EvtFilter(Sender:QObjectH; Event:QEventH):Boolean; var QMS:QMimeSourceH; S:WideString; begin Result:=False; if QEvent_isQDropEventEvent(Event) then begin QMS:=QDropEvent_to_QMimeSource(QDropEventH(Event)); QDropEvent_acceptAction(QDropEventH(Event),QTextDrag_canDecode(QMS)); if QTextDrag_canDecode(QMS) then begin ListBox1.Items.Clear; QTextDrag_decode(QMS,@S); ListBox1.Items.Add(S); end end else if QEvent_isQCloseEvent(Event) then QEvent_hook_destroy(Evt); end; procedure TForm1.Button1Click(Sender: TObject); begin QWidget_setAcceptDrops(ListBox1.Handle,True); Evt:=QEvent_hook_create(Self.ListBox1.Handle); //Filter:=Self.EvtFilter; TEventFilterMethod(H):=EvtFilter; Qt_hook_hook_events(Evt,H); end; procedure TForm1.FormDestroy(Sender: TObject); begin QEvent_hook_destroy(Evt); end; end.

Послесловие

Сперва хочу предупредить тех, кто уже сидит в Kylix'е читая эту статью: в портированном Borland экземпляре Qt.pas(в версии Kylix 1.0, а может и в других) неправильно определена функция QEvent_isQDropEventEvent. Для правильной работы вам необходимо в модуле Qt.pas в секции реализации заменить имя функции на которую ссылается QEvent_isQDropEventEvent c "QEvent_isQDropEventEvent" на "QEvent_isQDropEvent".

Несколько слов для тех, кто хочет сам попробовать написать универсальный компонент, для работы с Drag & Drop. Во первых - дерзайте ибо лучше наступить на свои "грабли" нежели на чужые (будет меньше обиды если что не так). Не забывайте освобождать занятые ресурсы, и если вы пишите в моем стиле - будьте осторожны при отладке компонента и при закрытии приложения. Ну а если уж совсем ничего не получается, то используйте мой компонент.

А для юзающих Kylix скажу, что здесь также можно "пойти другим путем" - созданием собственного компонента, на который бы покладалась задача по захвату событий и освобождению занятых ресурсов по окончании работы, мы бы только трудились над реакцией компонента на те или иные события. Некое подобие такого компонента есть в модуле QControls.pas под названием TX11DragFilter, и хотя этот компонент не подходит под концепцию "визуального программирования", но может послужить основой для создания собственных компонентов.


http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное