Qt-Forth - учебный forth с графикой Qt для Windows и Linux |
Учиться никогда не поздно,
тем более классическим алгоритмам. |
SPF 4.20 и QT
|
Подключение графической библиотеки QT к SPF 4.20
Введение. Зачем это нужно.
Форт интересный, необычный язык, однако его консольный вариант не выглядит современным решением. Необходим графический интерфейс, причём работающий и в Windows и в Linux. К тому же, желательно иметь возможность использовать различные, готовые механизмы, как то сетевые интерфейсы, XML, OpenGL и т.д. Все перечисленные возможности, как то кросплатформенность, а так же различные современные алгоритмы содержит библиотека QT. Именно её подключением к форту мы и будем заниматься. Данный пример работы с С++ библитекой, которой является QT, можно рассматривать как вариант общего подхода для работы с динамическими библиотеками C++ содержащими в разнообразных DLL и SO.
В качестве форта будем использовать SPF-4.20. Он доступен для Windows и Linux, имеет открытые исходные тексты, активно поддерживается рускоязычным сообществом. Содержит большое количество отлаженных кросплатформенных библиотек.
Постановка задачи. Необходимость в DLL и вызовах методов.
Итак, наша цель научиться использовать из SPF возможности QT. Хотя теоритически можно использовать С++ библиотеки непосредственно из форта, моделируя работу С++ (пример с.f), но практически это очень сложно. Главные трудности, как мне кажеться, это трдность с подсчетом размера памяти для создаваемого объекта, а так же трудность в создании и перекрытии виртуальных функций в С++ средствами форта. В связи с этим нам придется использовать собственную динамическую библиотеку FQT. Естественно, что для Windows это будет FQT.DLL а для Linux fqt.so Писать её будем на С++.
Я использовал MSVC-6.0 для Windows и gcc для Linux, и соответственно на их синтаксис я буду ориентироваться в примерах. С другой стороны полностью переписать (обернуть в собственные вызовы) QT практически невозможно. По этому, я использовал смешанный подход. Динамическая библиотека FQT (которую мы напишем) используется для создания объектов, перекрытия виртуальных методов, вызова обработчиков событий. Однако с форта, мы будем напрямую вызывать методы объектов QT, прямо по их адресам в DLL-ках. Такой подход позволяет минимизировать объём работ в С++, при создании FQT, и к тому же добавлять методы можно прямо из форта, не прибегая к С++.
Различные типы вызовов. CDECL, WINAPI, THIS-Call.
Для возможности вызова функций из динамических библиотек, нам необходимо иметь возможность организовывать различные типы вызовов непосредственно из SPF. Сам вызов, это CALL (в терминах ассемблера), а типы вызовов, это способ передачи параметров, через стек, через регистры и т.д.
stdcall/winapi - широко используется в Windows cdecl-call - С++ стандарт this-call - для работы с объектами С++.Вызовы типа this-call содержат неявный указатель на данные объекта, передаваемый в вызовах данного типа. Проблема в том, что различные компиляторы по разному организуют данные типы вызовов. Как я говорил выше, мы будем ориентироваться на MSVC 6.0 и gcc. Таким образом нам нужны вызовы:
CDECL-Call - Стандарт для C++ STDCALL-Call - Работа с WinAPI WINAPI-Call - Работа с WinAPI Extern - Работа с глобальными переменными THIS-CDECL-Win-VC-Call - Работа с объектами в MS VisualC 6.0 (windows) THIS-CDECL-Linux-gcc-Call - Работа с объектами в gcc (linux)Для организации вызовов типа CDECL-Call, STDCALL-Call, WINAPI-Call – воспользуемся словами PAS-EXEC и C-EXEC из библиотеки SPF ~ac\lib\ns\so-xt.f Для организации вызова THIS-CDECL-Win-VC-Call воспользуемся словом THIS-CDECL-CALL-ECX написанном на встроенном ассемблере. Алгоритм этого вызова такой же, как и у CDECL-Call, но указатель на объект передаётся в регистре ECX. Для THIS-CDECL-Linux-gcc-Call тоже что и CDECL-Call, но указатель на объект первый в списке аргументов.
Более подробно о вызовах http://www.programmersheaven.com/2/Calling-conventions
Так же следует помнить, что в динамических библиотеках C++ используется искажение имен функций, и метод QWidget::show() будет назван ?show@QWidget@@QAEXXZ или что то похожее.
Модуль загрузки библиотек. Двойственность загрузки библиотеки.
Теперь, когда понятно, как использовать вызовы, нужно знать самое главное, а именно адрес на который передать управление. Файлы DLL (Windows) и SO (Linux) как раз и содержат готовые к применению функции, которые мы и будем вызывать. Обычно, при работе на С++ или других языках программирования, компилятор берет на себя всю работу, по поиску, загрузке и вызову нужных функций. Мы же в SPF всё это будем делать сами.
Программа явно может загружать и выгружать динамические библиотеки. Делает она это при помощи функций API:
- Для Windows: LoadLibrary("Имя библиотеки"...)
- Для Linux: dlopen("Имя библиотеки"...)
Когда библиотека загружена в память, следующим этапом надо найти адреса нужных функций. Динамическая библиотека содержит таблицу имен функций. Есть много утилит которые показывают эту таблицу. Для Linux : objdump -t имя.so например. Адрес функции, это адрес куда надо передать выполнение для выполнения функции. Ищем этот адрес:
- Для Windows: GetProcAddress("Имя функции"...)
- Для Linux: dlsym("Имя функции"...)
Если вызов прошел удачно, то на выходе будет адрес нужной нам функции. По этому адресу можно её вызвать. Перед вызовом нужно положить в стек параметры, а после вызова забрать результат выполнения, если он есть.
При работе в форте, надо учитывать факт того, что программа форта работает как бы в двух режимах. Первый, непосредственно интерпретация программы на форте, а второй – это работа исполняемого модуля (EXE) после компиляции программы. Так вот загрузка динамических библиотек должна учитывать такую двойственность. Я долго не мог понять, почему запуск spf4 Имя.f работает правильно, а EXE модуль Имя.EXE валится по ошибке.
Другими словами, загрузку библиотеки надо осуществлять непосредственно в момент работы готовой программы, перед использованием ей вызова внешних функций.
Для обеспечения условия правильной загрузки, а так же для упрощения читабельности кода на форте и для обеспечения гибкости вызовов применяется несколько слов на форте (Library”, _-Call" и т.д.), реализующих следующий алгоритм:
- Library" libc.so.6" libc – декларация имени загружаемой библиотеки. Создаётся новое определяющее слово libc. Создаётся пустой список с именами функций. Ни какой загрузки библиотеки в данный момент нет.
- Library@ libc Nкол_аргументов ТИП_ВЫЗОВА” fopen” fopen – добавляется элемент в список, содержащий имя функции, тип вызова и количество параметров. Ни какой загрузки библиотеки в данный момент нет.
- LibraryLoad libc – именно в этот момент происходит реальная загрузка динамической библиотеки. Далее перебирая список, созданный на предыдущем этапе, осуществляется поиск и сохранение адресов функций. Таким образом слово fopen (см. пред. описание) запомнит адрес для вызова, количество аргументов и тип вызова.
Обращаю особое внимание, при создании файлов EXE, обязательно перед реальным использованием в EXE вызовов функций из динамической библиотеки, необходимо осуществлять загрузку её в память: LibraryLoad libc (в качестве примера).
Объекты в SPF и обоснование их применимости.
Имея подключенные внешние функции, можно писать любые программы. Но, пользоваться прямо такими вызовами не удобно, громоздко. Воспользуемся библиотекой ~day\hype3\hype3.f для организации объектно-ориентированного (ОО) механизма. Мы должны для каждого созданного объекта QT (C++) запомнить его адрес непосредственно в форте, что бы потом передавать его в качестве аргумента внешним функциям. Все объекты у нас описываются похожим образом. Рассмотрим типичное описание на примере QWidget.
// Базовый класс QWidget, на него замыкаются другие виджеты CLASS fQWidget 1 CELLS PROPERTY adr_fQWidget // Запомним адрес объекта QWidget : create // Инициализация класса 0 СоздатьОкно // Создать объект QWidget в С++ и его адрес на стек adr_fQWidget ! // Запомнить адрес QWidget ; : show // Показать окно adr_fQWidget @ ПокажиОкно DROP // вызов внеш. функции и сброс кода возврата ; // Здесь СоздатьОкно – это вызов внешний функции типа CDECL-Call с 1 параметром: Library" fqt.dll" libfqt // Грузанем нашу библиотеку в память Library@ libfqt 1 CDECL-Call" QT_QWidget" СоздатьОкно // Здесь ПокажиОкно – это вызов внешней функции QWidget.show(). Library" QtGuid4.dll" QtGui // Грузанем библиотеку Qt в память Library@ QtGui 0 THIS-CDECL-Win-VC-Call" ?show@QWidget@@QAEXXZ" ПокажиОкно // ПокажиОкноТаким образом, вызовы QT мы включаем в нашу объектную иерархию. Так как в QT объекты связаны наследованием, мы тоже будем наследовать объекты. Это сделано для того, что бы можно было использовать методы QWidget (базовый класс) для других клссов, например QTextEdit. Все визуальные классы наследуют метод show, определенный в классе QWidget. Конечно повторить всю QT мы не можем, но в состоянии довольно быстро добовлять нужные методы и классы по мере необходимости.
Архитектура нашей программы.
+----------------------+ +---------------------------------+ |Наша программа на SPF |----явно----->| fqt.dll (наша всп. библ. на C++ | +----------------------+ +---------------------------------+ | | | +---------------------------------+ | +---явно---->| QtGui.dll, QtCore.dll и т.д. | <----+ неявно через С++ +---------------------------------+Наша программа на SPF взаимодействует с fqt.dll для тех действий, которые тяжело повторить на форте и взаимодействует прямо с QT (QtCore.DLL, QtGui.DLL) для подключения методов (см описание QWidget выше). Обратите внимание, на искажение имён (?show@QWidget@@QAEXXZ) вносимое компилятором С++. В этом имени зашифрован тип вызова и кол параметров. Для разных компиляторов может быть различным. Почему я решил использовать именно такую архитектуру? По тому, что без spf.dll не обойтись, нужно перекрывать виртуальные функции C++ и обрабатывать события, а с другой стороны всю QT запихать в spf.dll нет возможности. К тому же описать в SPF новый метод очень просто и для этого не нужно менять fqt.dll. Это позволяет свести к минимуму работу с C++.
Основное приложение QT. Главная процедура. Ревызов из DLL.
Вот мы и подошли к очень важному, принципиальному моменту. Правильная последовательность вызовов. Честно сказать, я почти полгода не мог найти правильную последовательность вызовов QT. А всё потому, что думал на C++, а не на ассемблере :(. Для того, что бы понять суть, рассмотрим работу с QT из С++.
Типичная (минимальная) программа: #include// Включить в программу файлы заголовков int main(int argc, char *argv[]) { QApplication app(argc, argv); // Инициализация Qt ..... работа с графикой ........ return app.exec(); // Цикл опроса графических событий } Как видно, есть всего две функции (QApplication и app.exec), которые как скобки обрамляют основное тело программы. В начале я пытался просто вызвать эти две функции из SPF (смотри http://mgw.narod.ru/algoritms.htm#SPF-QT, и даже что то работало. Однако программа валилась при любой возможности. После исследования работы под отладчиком MSVC 6.0 данного варианта, я понял, что проблема в наборе значений регистров в процедуре main(). Совершенно точно надо было обеспечить сохранность и неизменность регистров EBP и некоторых других между вызовами QApplication и app.exec(). Так как из SPF это сделать проблематично, я подумал, а почему бы не передать вызов непосредственно в main(), а уже из неё вызвать форт. Таким образом алгоритм взаимодействия SPF и QT следующий:
(SPF) (C++) fqt.DLL ============= ================= : адрSPF // CALLBACK <-------+ Создать QWidget ----------|----------> СоздатьQWidget() Создать ...... | { ; | return NEW QWidget(); | } | : mainSPF | адрSPF argv argc | ИнициализацияQT ----------|----------> ИнициализацияQT(argc,argv,адрSPF) ; | { | app=QApplication app(argc,argv); ----------------Вызов адрSPF return app.exec() }Таким образом, ИнициализацияQT в fqt.dll выступает как функция main(), из которой вызываются различные процедуры (SPF). Так как С++ за нас сохраняет всё что ему нужно между вызовами QApplication и app.exec(), то всё сразу стало нормально работать. Приведенное выше взаимодействие с QT работает нормально не только в SPF но и других языках программирования. Следует отметить, что для SPF пришлось модернизировать работу ИнициализацияQT(argc,argv,адрSPF). Прямой вызов не работал и заработал только когда я вставил сохранение регистров.
ИнициализацияQT(argc,argv,адрSPF) { app=QApplication app(argc,argv); _asm{ PUSH EBP PUSH EAX } Вызов адрSPF _asm{ POP EAX POP EBP } return app.exec() }Данная последовательность позволяет нам сохранить состояние нужных регистров перед вызовом CALLBACK и востановить их после вызова.
CALLBACK и их параметры. Виртуальные методы в объектах С++
К этому моменту, мы разобрали почти все принципиальные моменты построения связки между SPF и QT. Однако есть ещё одна проблема. Объекты QT используют большое количество виртуальных методов. Программируя на С++ мы должны перекрыть их, что бы обеспечить их функциональность в рамках нашей задачи. Вопрос, как это сделать с форта? Я не знаю. По этому, я просто в fqt.DLL сам перекрываю эти виртуальные методы, заставляя их вызвать функцию (слово) SPF. Адрес для вызова сохраняется в переменных (полях данных или свойствах) конкретного объекта. Посмотрим это на примере организации объекта QWidget.
Пример. Нам надо организовать объект QWidget с дополнительным свойством вызова функции на событие изменение размера самого окна. Почитав документацию к QT видим, что существует виртуальный метод void QWidget::resizeEvent(QResizeEvent *), который будет вызван при изменении размера окна. Т.к. как с SPF перекрыть такой метод я не знаю, то приходится вводить следующую конструкцию в fqt.DLL
class zQWidget : public QWidget { Q_OBJECT public: zQWidget( QWidget* parent = 0 ); ~zQWidget(); void *aOnResize; // Сохраняет адрес слова CALLBACK форта void resizeEvent( QResizeEvent * ); // наш обработчик события }; void zQWidget::resizeEvent( QResizeEvent *a ) // Тело нашего обработчика { // если сохр адрес не нулевой, то вызови функцию в SPF if (aOnResize!= NULL) ((ExecZIM_1_0)aOnResize)((void *)a); } // !!! Установить обработчик на resizeEvent extern "C" FQT_API void QT_QWidget_onresize(zQWidget* qw, void *uk) { qw->aOnResize = uk; }Таким образом мы ввели в fqt.DLL возможность обработать событие «изменение размера окна». Для правильной обработки этого события из SPF нам надо:
- создать в SPF CALLBACK который будет обрабатывать данное событие
- установить (активизировать) обработчик, записав адрес обработчика в поле объекта. Для этого используется внешняя функция QT_QWidget_onresize. Соответственно, если мы запишем 0 в адрес обработчика, то отключим обработку события. По умолчанию, обработка событий отключена.
Отладка DLL и SPF. Работа с отладочными словами. INT3
Важная проблема, при разработке такого комплекса, это отладка. Для примера расскажу, как я вёл отладку в MSVC 6.0. Текст библиотеки fqt.DLL написан на С++. Значит нам надо просто вызвать данную библиотеку на выполнение и с SPF вызывать нужные функции. Отладка DLL на С++ описана подробно в Интернете. Нам надо просто во вкладках проекта установить, что основная программа будет SPF с параметрами (spf4 console.f). При запуске на выполнение из IDE MSVC будет сообщение, что SPF не имеет отладочной информации, но нам это не принципиально. Главное, если мы поставим точку остановки на текст DLL – отладчик включится при достижении этого места. Таким образом мы можем контролировать приходящие из SPF параметры и вести отладку нашей fqt.DLL.
Кстати, ни чего не мешает нам вести отладку ассемблерных слов в SPF, используя для этого MSVC (или любой другой отладчик). Что такое «точка останова» в терминах ассемблера? Это просто машинная команда INT 3, встретив которую управление передаётся отладчику. Просто в SPF определяем слово:
CODE BreakPoint // Прерывание для внешнего отладчика INT 3 RET END-CODEТеперь встретив это слово, произойдет прерывание и переход в отладчик, где можно спокойно дизассемблировать код, посмотреть память или состояние регистров, выполнить программу по шагам и т.д. и т.п. В Linux похожим образом я использовал отладчик gdb и ddd.
Основной шаблон графической программы.
Теперь, когда мы разобрали основные части нашей библиотеки, поговорим о минимальной программе с использованием графики QT. Но прежде пару слов о подключении DLL или SO. Для Windows всё просто, поместите fqt.DLL в каталог с самой программой (сохраненной по SAVE) или в каталог SPF. Поиск DLL в Windows начинается с каталога программы. А вот в Linux это не так. Там поиск библиотеки происходит по строго определенному маршруту. Для включения в этот маршрут каталога программы я использую переменную окружения:
LD_LIBRARY_PATH=`pwd`; export LD_LIBRARY_PATH
Запуск данной конструкции присваивает переменной LD_LIBRARY_PATH текущий каталог и делает её видимой другим программам. Таким образом, текущий каталог становится доступным для поиска fqt.so которая содержит связку с QT Linux.
Итак минимальная программа должна включать:
Подключить определения
- 1 – Ассемблерных вызовов THIS-CDECL-CALL-ECX, THIS-CDECL-CALL и т.д.
- 2 – Подключить вызовы PAS-EXEC и C-EXEC (из ~ac\lib\ns\so-xt.f)
- 3 – Подключить работу с DLL и SO: Library” –Call” CDECL-Call" и т.д.
- 4 – Декларируем загрузку FQT.DLL (FQT.SO)
- 5 – Декларируем точки вызова нужных функций и методов QT
- 6 – Определяем классы для работы с QT используя ~day\hype3\hype3.f
Создать объекты- 7 – Создаём объекты используя NEW
Создать CALLBACKи- 8 – Создаём CALLBACK для обработки вызова из стартовой функции FQT.DLL
- 9 – Создаём CALLBACKи для обработки виртуальных вызовов, если они нужны
Программируем логику- 10 – Определяем слово main (стартовое) где:
Действия по выходу из графики. SAVE программы.
- Грузим декларированные библиотеки LibraryLoad libfqt
- Вызываем стартувую функцию в FQT: aonForth @ ARGV ARGC СоздатьПриложение // Инициализация QT
Получилось не мало, но и задача сложна. К тому же большинство действий можно описать только один раз, а потом подключать в файле. Я стараюсь писать подробно, что бы вы поняли суть подхода. Ни чего не мешает подключить это всё к любому другому форту или вообще к другому языку программирования. Да и всё равно, это намного короче, чем программировать на Win API, а для Linux вообще реальной альтернативы нет.
Главное, на выходе реальный кросплатформенный текст на SPF одинаково работающий и в Windows и в Linux!
Графическая консоль, как пример работы с библиотекой.
В качестве примера посмотрим на текст графической консоли console.f Для успешной работы с QT надо представлять, как взаимодействуют объекты внутри неё. Так как FQT.DLL всего лишь обертка, она не скрывает внутренности QT. Одним словом, надо понимать, что такое Widget, layout и т.д.
Графическая консоль представляет собой окно типа QMainWindow, в которое вставлен главный выравниватель QLayout, в который в свою очередь вставлены редактор QTextEdit и строка ввода QLineEdit. Так же в QMainWindow вставлена статусная строка QStatusBar. Для строки ввода QLineEdit определён CALLBACK для события нажатия на Enter. Его задача, прочитать строку из строки ввода, скопировать её в окно редактора и выполнить через EVALUATE. Следует обратить внимание на тот факт, что все объекты QT работают со строками типа QString. По этому везде ведется преобразование между обычными строками SPF и QString.
Пример слово : TYPE_W // ( As Nstr -- ) Вывести строку SPF в окно QTextEdit drop qs1 set // qs1 объект QString запоминает строку методом set qs1 @ te1 append // te1:QTextEdit забирает qs1:QString и вставляет append ;Так же в тексте программы console.f определены слова для отладки. Это модальный диалог и окно распечатки стека. Они останавливают ход программы и позволяют посмотреть значения на стеке, распечатать дамп памяти или просмотреть значение переменных.
Заключение.
Я этой короткой статье я попытался описать способ подключения графической библиотеки QT к SPF. Осталось неосвещенными множество моментов, как то динамическое создание объектов, конструкторы и деструкторы и т.д. и т.п. Надеюсь, что это еще всё впереди. Пока удалось достичь главного. Форт (SPF 4.20) получил возможность использования современной кросплатформенной графики.
Все исходные тексты открыты и доступны на сайте http://mgw.narod.ru
Краткое описание и примеры программ SPF 4.20 + Qt
Установка библиотеки.
Дистрибутив состоит из архива:
- spfqt_win.zip - для Windows.
- spfqt_linux.zip - для Linux.
Архив для Windows содержит каталог ~mgw (форт часть) который нужно скопировать в каталог devel SPF. fqt452.dll mingwm10.dll а так же QtCore.dll и QtGui.dll (QtCore.dll и QtGui.dll можно скачать с http://www.mgw.narod.ru/download.htm) - положить в каталог SPF.
Если всё правильно, то можно выполнить console.f - появится окно графической консоли, в котрой уже можно довать команды форта на выполнение.
Шаблон графической программы.
В качестве шаблона графической программы можно использовать ~mgw\example\tutor1.f Рассмотрим его подробнее.
REQUIRE MGW ~mgw\stdlib.f \ Поддержка QT. Загружает необходимые слова для работы с Qt fQApplication NEW app1 \ Обязательная ссылка на QApplication. \ ----------- Место определения объектов ------------------ fQString NEW qs1 \ Объект Строка fQTextEdit NEW te1 \ Объект ЭкранныйРедактор \ NEW фактически равен VARIABLE. В этот момент нет вызова DLL \ ----------- Место определения CALLBACK обработки сообщений QT --------- :NONAME \ ( Aapp -- ) Обязательная Главная процедура работы с QT, тот же main() в примерах на С++ app1.create \ Обязательно Запомнить указатель на Application \ -- Тело программы, инициализация, начальные действия -- qs1.create \ создадим QString. Вызов DLL - реальный вызов конструктора Qt te1.create \ создадим QTextEdit. Вызов DLL - реальный вызов конструктора Qt S" Здравствуй МИР!!!" DROP qs1.set qs1.@ te1.append te1.show \ Отобразим виджет на экране монитора \ ----- Конец инициализации и начальных действий --------- app1.@ \ Положить на стек параметр из DLL 0 \ возвращаемое значение (треб SPF) ; 1 CELLS CALLBACK: onForth \ Фактически создали процедуру инициализ Qt VARIABLE aonForth ' onForth aonForth ! \ ----------- Конец определений CALLBACK обработки сообщений QT --------- : run \ Стартовое слово форта \ Грузим необходимые DLL. В этот момент заполняются ссылки на функции DLL LibraryLoad user32 LibraryLoad msvcrt \ Проверка на доступность, нужна для QtGui LibraryLoad mingwm10 \ Проверка на доступность, нужна для QtGui LibraryLoad QtGui LibraryLoad QtCore LibraryLoad libfqt aonForth @ GetCommandLineA ASCIIZ> args SWAP фСоздатьПриложение DROP BYE ; \ ----------- .. ну а дальше, всё как обычно в форте --------- \ run ' run MAINX ! DIS-OPT S" tutor1.exe" SAVE BYEПоддержка объектов построена на библиотеке ~day\hype3\hype3.f. На этой основе сделанн следующий синтаксис работы с объектами:
Класс NEW объект \ Создать объект в форте [возможные параметры] объект.create \ создание объекта в C++ объект.методы \ вызов метода объектаQWidget - Основа основ.
Почти вся работа Qt строится на понятии Widget. Это область на экране монитора. Фактически все визуальные элементы наследуются от класса QWidget. Создание и отображение объектов этого класса в форте fQWidget
Давайте сделаем виджет. Для этого запустим графическую консоль (spf4.exe console.f) из библиотеки. Консоль фактически представляет собой шаблон программы (см шаблон графической программы), в который вставлена функция чтения строки и выполнения её в EVALUATE. За счет этого можно сразу видеть результат выполнения наших команд на форте. Наберем в консоли строку (можно перенести через буфер объмена):
fQWidget NEW окно окно.create окно.show \ Создадим окно и отобразим егоНа мониторе увидим новое окошко. Посмотрим некоторые свойства данного виджета.100 200 окно.resize \ Изменим размер 10 10 окно.move \ передвинем его в левый верхний угол монитороаВозникает вопрос, как посмотреть, что ещё можно сделать с окном (фактически с любым виджетом). Для этого смотрим реализацию класса fQWidget (файл ~mgw\stdlib.f) и видим там ещё некоторое количество методов. С простыми методоми всё понятно, но есть и сложные.
Сложные, это когда событие инициирует сама Qt. Событий в Qt много, представлены они виртуальными функциями на C++. В файле fqt452.dll некоторые из них перехвачены и их обработка выведена в форт. Таким образом, для обработки события нам надо указать Qt, куда (на какое слово) передать управление для обработки данного события. Поскольку вызывает Qt в форте эти слова реализованы в виде CALLBACK.
Создадим файл pr1.f
\ Пример работы с fQWidget \ Вызывать только из графической консоли fQWidget NEW окно окно.create окно.show \ Обработка события OnResize. Для подробного объяснения CALLBACK см. документацию VARIABLE aonResize :NONAME \ ( A -- ) Обработка события resize S" ." TYPE 0 ; 1 CELLS CALLBACK: onResize ' onResize aonResize ! \ aonResize - это переменная с адресом нашего обработчика aonResize @ окно.onresize \ установим обработчик \ Конец файла pr1.fПри изменении размера нашего созданного окна, в поток выводится точка (S" ." TYPE), что и видно на экране