Руководство программиста в Photon (953920), страница 43
Текст из файла (страница 43)
void *data);
где аргументы следующие:
app Адрес контекста приложения, структуры типа PtAppContext_t, управляющей всеми данными, связанными с этим приложением. Задайте NULL в качестве этого аргумента, так чтобы использовался контекст по умолчанию.
set Указатель на набор сигналов, которые должны служить причиной вызова функции обработки сигналов. Для компоновки этого набора используйте функции sigemptyset() и sigaddset(). Более подробно см. в "Справочнике библиотечных функций QNX 6".
func Функция обработки сигналов. См. описание PtSignalProc_t в "Справочнике библиотечных функций Photon'а".
data Любые данные, передаваемые функции
Функция PtAppAddSignalProc() возвращает 0 в случае успеха или –1, если случается ошибка.
Ваша функция обработки сигнала имеет следующий прототип:
int signalProcFunctions(int signum, void *data);
аргументами являются:
signum Номер обрабатываемого сигнала
data Параметр data, заданный в вызове функции PtAppAddSignalProc().
Если Вы хотите, чтобы обработчик сигнала остался установленным, верните Pt_CONTINUE. Чтобы удалить его для текущего сигнала, верните Pt_END (если функция была зарегистрирована и для других сигналов, она по-прежнему будет вызываться, если те появятся).
Удаление функции обработки сигналов
Чтобы удалить функцию обработки сигнала:
-
Вызовите функцию PtAppRemoveSignal(), чтобы удалить одну или все наличествующие пары (функция обработки сигналов, данные).
-
Верните Pt_END функцией обработки сигнала. Если функция была зарегистрирована для более чем одного сигнала, она останется установленной для остальных сигналов, кроме того, который как раз обрабатывался.
Другие механизмы ввода/вывода
Если Вашему приложению необходимо выполнить ввод/вывод, такие как чтение или запись в трубопровод, Вы должны добавить обработчик файлового дескриптора (fd handler). Обработчик файлового дескриптора – это функция, вызываемая в главной петле событий, когда заданный файловый дескриптор (fd) готов к вводу или выводу:
-
Чтобы добавить обработчик файлового дескриптора в Ваше приложение, вызовите функцию PtAppAddFd() или PtAppAddFdPri().
-
Подробности о прототипе обработчика файлового дескриптора см. в описании PtFdProc_t в "Справочнике библиотечных функций Photon'а"
-
Чтобы изменить режим, являющийся сферой интересов обработчика файлового дескриптора, вызовите функцию PtAppSetFdMode().
-
Чтобы удалить обработчик файлового дескриптора, верните из него Pt_END или вызовите функцию PtAppRemoveFd().
Эти функции описаны в "Справочнике библиотечных функций Photon'а".
Если обработчик файлового дескриптора изменяет изображение, он должен вызвать функцию PtFlush(), чтобы гарантировать обновление изображения.
Глава 17. Параллельные операции
В этой главе обсуждатся:
-
Обзор
-
Потоки
-
Рабочие процедуры
-
Фоновое исполнение
Обзор
Когда Вы выполняете операцию, отнимающую на исполнение много времени, выполнять её как простую ответную реукцию является не лучшей идеей. Во время исполнения ответной реакции виджеты Вашего приложения не будут восстанавливать свои повреждения и совсем не будут откликаться на действия пользователя. Вам необходимо разработать стратегию обработки очень длинных операций внутри внутри Вашего приложения, так чтобы возвращать управление из Вашей ответной реакции как можно скорее.
Возврат управления из Вашей ответной реакции позволяет виджетам продолжить визуальное самообновление. Это также даёт некую визуальную обратную связь с пользователем, когда тот попытается что-то сделать. Если Вы не хотите, чтобы пользователь мог выполнить в это время какую-либо операцию пользовательского интерфейса, Вы должны деактивировать меню и кнопки команд. Вы можете сделать это установкой флага Pt_BLOCKED в ресурсе Pt_ARG_FLAGS виджетов окна приложения.
При распараллеливании операций Вы можете рассмотреть применение одного из нескольких различных механизмов:
-
Если Вы не можете разбить операцию на отдельные части, обрабатывайте событитие Photon'а во время выполнения операции; См. раздел "Фоновое исполнение" ниже.
-
Если Вы можете разбить операцию на маленькие куски программы, Вы можете захотеть иметь функцию, которая отслеживает текущее состояние и исполняет один маленький кусок операции за раз. Вы можете затем установить виджет таймера и подсоединить его к ответной реакции, которая бы вызывала функцию каждый раз, когда таймер бы срабатывал. Или же Вы можете вызывать функцию из так называемой рабочей процедуры. Эти методы особенно эффективны для многократно повторяющихся опреаций, когда функция может быть исполнена один раз в итерации. См. раздел "Рабочие процедуры" ниже.
-
Используйте множественные кнопки. Это требует определённой специфической обработки, поскольку библиотеки Photom'а не являются потокобезопасными (thread-safe); см. раздел "Потоки" ниже.
-
Породите в ответной реакции другой процесс, и пусть имеется другой процесс, возвращающий результаты первого процесса приложению путём отсылки его сообщений. В этом случае очень важно быть в состоянии отслеживать протекание операции и давать пользователю визуальную обратную связь.
Фоновое исполнение
Если очень длительная операция не может быть легко разбита на отдельные части, и Вы не хотите использовать несколько потоков, Вы должны по меньшей мере вызвать функцию PtBkgdHandlerProcess() для обработки событий Photon'а, так чтобы графический интерфейс пользователя не выглядел бы замороженным.
Если операция ну очень длительная, Вы можете вызвать PtBkgdHandlerProcess() внутри петли. Как часто Вам надо вызывать функцию PtBkgdHandlerProcess(), зависит от того, что делает Ваше приложение. Вы должны также найти способ, позволяющий пользователю знать, в каком состоянии ход исполнения операции.
Например, если Вы читаете большую директорию, Вы можете вызывать фоновый обработчик после чтения нескольких файлов. Если Вы открываете и обрабатываете каждый файл в директории, Вы должны вызывать PtBkgdHandlerProcess() после каждого файла.
Безопасным является вызов функции PtBkgdHandlerProcess() в ответных реакциях, рабочих процедурах и процедурах ввода, но отнюдь не в "Draw"-методе виджета (см. книгу "Проектирование своих виджетов" или в функции прорисовки PtRaw).
Если ответная реакция вызывает функцию PtBkgdHandlerProcess(), будьте осторожны, если можете вызвать ответную реакцию одновременно несколько раз. Если Вы не хотите обрабатывать эту рекурсию, то должны блокировать виджет(ы), связанный(ые) с ответной реакцией.
События Photon'а обрабатываются следующими функциями:
-
PtBkgdHandlerProcess()
-
PtFileSelection()
-
PtProcessEvent()
-
PtSpawnWait()
Рабочие процедуры
Рабочая процедура выполняется всякий раз, когда для Вашего приложения нет сообщений, на которые надо реагировать. В каждом цикле петли обработки событий Photon'а эта процедура вызывается, если не получены никакие сообщения (лучше, чем блокироваться на ожидании последующих сообщений функцией MsgReceive() ). Эта процедура будет выполняться очень часто, так что стремитесь сохранить её как можно более короткой.
Если Ваше рабочая процедура изменяет изображение, вызывайте функцию PtFlush(), чтобы гарантировать обновление изображения.
См. раздел "Потоки и рабочие процедуры" ниже, если Вы пишете рабочую процедуру для многопоточной программы.
Рабочие процедуры собираются в стек; когда Вы регистрируете рабочую процедуру, она помещается наверх стека. Вызывается только рабочая процедура, расположенная в вершине стека. Если вы удаляете рабочую процедуру, находящуюся в вершине стека, вызывается эта, что расположена сразу под ней.
Рабочая процедура сама по себе является функцией ответной реакции, получающей единственный void* параметр – client_data. Этот параметр client_data представляет собой данные [вернее, указатель на данные. – Прим. пер.], которые Вы присоединяете к рабочей процедуре, когда регистрируете её в библиотеке виджета. Вы должны создать структуру данных для рабочей процедуры, которая содержит вся её информацию о состоянии, и предоставить её как client_data.
Чтобы зарегистрировать, или добавить, рабочую процедуру, вызовите функциюPtAppAddWorkProc():
PtWorkProcId_t * PtAppAddWorkProc( PtAppContext_t app_context,
PtWorkProc_t work_func,
void *data );
параметрами которой являются:
-
app_context – адрес контекста приложения, структура типа PtAppContext_t, которая управляет всеми данными, присоединёнными к этому приложению. Этот параметр должен быть задан как NULL, так чтобы использовался контекст по умолчанию.
-
work_func – адрес функция ответной реакции – рабочая процедура. См. описание PtWorkProc_t в "Справочнике библиотечных функций Photon'а".
-
client_data – данные, передаваемые функции при её вызове.
PtAppAddWorkProc() возвращает указатель на структуру типа PtWorkProcId_t, которая идентифицирует рабочую процедуру. Чтобы удалить рабочую процедуру, когда она уже больше не нужна, вызывайте PtAppRemoveWorkProc():
void PtAppRemoveWorkProc( PtAppContext_t app_context,
PtWorkProcId_t *WorkProc_id );
передавая ей тот же контекст приложения и указатель, возвращённый функцией PtApppAddWorkProc().
Реальный пример использования рабочей процедуры слишком велик, чтобы размещать его здесь, поэтому вот простой итеративный пример. Рабочая процедура отсчитывает большое число, периодически обновляя надпись, чтобы отражать ход её исполнения.
#include <Pt.h>
typedef struct workDialog {
PtWidget_t *widget;
PtWidget_t *label;
PtWidget_t *ok_button;
} WorkDialog_t;
typedef struct countdownClosure {
WorkDialog_t *dialog;
int value;
int maxvalue;
int done;
PtWorkProcId_t *work_id;
} CountdownClosure_t;
WorkDialog_t *create_working_dialog(PtWidget_t *parent) {
PhDim_t dim;
PtArg_t args[3];
int nargs;
PtWidget_t *window, *group;
WorkDialog_t *dialog = (WorkDialog_t *) malloc(sizeof(WorkDialog_t));
if (dialog) {
dialog->widget = window = PtCreateWidget(PtWindow, parent, 0, NULL);
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_GROUP_ORIENTATION, Pt_GROUP_VERTICAL, 0);
nargs++;
PtSetArg(&args[nargs], Pt_ARG_GROUP_VERT_ALIGN, Pt_GROUP_VERT_CENTER, 0);
nargs++;
group = PtCreateWidget(PtGroup, window, nargs, args);
nargs = 0;
dim.w = 200;
dim.h = 100;
PtSetArg(&args[nargs], Pt_ARG_DIM, &dim, 0); nargs++;
PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING, "Counter: ", 0); nargs++;
dialog->label = PtCreateWidget(PtLabel, group, nargs, args);
PtCreateWidget(PtSeparator, group, 0, NULL);
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING, "Stop", 0); nargs++;
dialog->ok_button = PtCreateWidget(PtButton, group, 1, args);
} // if(dialog)
return dialog;
} // create_working_dialog()
int done(PtWidget_t *w, void *client, PtCallbackInfo_t *call) {
CountdownClosure_t *closure = (CountdownClosure_t *)client;
call = call;
if (!closure->done) { PtAppRemoveWorkProc(NULL, closure->work_id); }
PtDestroyWidget(closure->dialog->widget);
free(closure->dialog);
free(closure);
return (Pt_CONTINUE);
} // done()
int count_cb(void *data) {
CountdownClosure_t *closure = (CountdownClosure_t *)data;
char buf[64];
int finished = 0;
if ( closure->value++ = = 0 || closure->value % 1000 = = 0 ) {
sprintf(buf, "Counter: %d", closure->value);
PtSetResource( closure->dialog->label, Pt_ARG_TEXT_STRING, buf, 0);
}
if ( closure->value = = closure->maxvalue ) {
closure->done = finished = 1;
PtSetResource( closure->dialog->ok_button, Pt_ARG_TEXT_STRING, "Done", 0);
}
return finished ? Pt_END : Pt_CONTINUE;
} // count_cb()
int push_button_cb(PtWidget_t *w, void *client, PtCallbackInfo_t *call) {