Руководство программиста в Photon (1037671), страница 65
Текст из файла (страница 65)
Ответная реакция done() вместо освобождения закрытия окна вызывает функцию PtModalUnblock():
int done(PtWidget_t *w, void *client, PtCallbackInfo_t *call) {
CountdownClosure *closure = (CountdownClosure *)client;
call = call;
if (!closure->done) {
PtAppRemoveWorkProc(NULL, closure->work_id);
}
PtDestroyWidget(closure->dialog->widget);
free(closure->dialog);
/* Новое: завершаем модальную петлю, возвращаем значение счётчика в качестве ответа */
PtModalUnblock(&(closure->modal_control), (void *) &(closure->value));
return (Pt_CONTINUE);
}
Всё, что осталось в этом месте сделать – это изменить функцию ответной реакции push_button_cb(), так чтобы она блокировала окно после реализации "прогрессного" диалога, запускала модальную петлю, разблокировала окна и освобождала закрытие после исчезновения диалога.
Вот новая версия функции ответной реакции push_button_cb():
int push_button_cb(PtWidget_t *w, void *client, PtCallbackInfo_t *call) {
PtWidget_t *parent = (PtWidget_t *)client;
WorkDialog *dialog;
PtBlockedList_t * blocked_list;
void * response;
w = w; call = call;
dialog = create_working_dialog(parent);
if (dialog) {
CountdownClosure *closure = (CountdownClosure *) malloc(sizeof(CountdownClosure));
if (closure) {
PtWorkProcId_t *id;
closure->dialog = dialog;
closure->value = 0;
closure->maxvalue = 200000;
closure->done = 0;
closure->work_id = id =
PtAppAddWorkProc(NULL, count_cb, closure);
PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE, done, closure);
PtRealizeWidget(dialog->widget);
/* Новое: блокируем все окна, кроме диалога, обработываем события вплоть
до закрытия диалога, и затем разблокируем все окна */
locked_list = PtBlockAllWindows (dialog->widget,
Ph_CURSOR_NOINPUT, Pg_TRANSPARENT);
response = PtModalBlock( &(closure->modal_control), 0 );
printf ("Достигнутое значение равно %d\n", *(int *)response );
free (closure);
PtUnblockWindows (blocked_list);
}
}
return (Pt_CONTINUE);
}
А вот новая версия программы в целом:
#include <Pt.h>
typedef struct workDialog {
PtWidget_t *widget;
PtWidget_t *label;
PtWidget_t *ok_button;
} WorkDialog;
typedef struct countdownClosure {
WorkDialog *dialog;
int value;
int maxvalue;
int done;
PtWorkProcId_t *work_id;
/* Новый член: */
PtModalCtrl_t modal_control;
} CountdownClosure;
WorkDialog *create_working_dialog(PtWidget_t *parent) {
PhDim_t dim;
PtArg_t args[3];
int nargs;
PtWidget_t *window, *group;
WorkDialog *dialog = (WorkDialog *)malloc(sizeof(WorkDialog));
if (dialog) {
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_WIN_PARENT, parent, 0);
nargs++;
PtSetParentWidget(NULL);
dialog->widget = window = PtCreateWidget(PtWindow, parent, nargs, args);
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);
}
return dialog;
} // create_working_dialog()
int done(PtWidget_t *w, void *client, PtCallbackInfo_t *call) {
CountdownClosure *closure = (CountdownClosure *)client;
call = call;
if (!closure->done) {
PtAppRemoveWorkProc(NULL, closure->work_id);
}
PtDestroyWidget(closure->dialog->widget);
free(closure->dialog);
/* Новое: завершаем модальную петлю, возвращаем значение счётчика в качестве ответа */
PtModalUnblock(&(closure->modal_control), (void *) &(closure->value));
return (Pt_CONTINUE);
} // done()
int count_cb(void *data) {
CountdownClosure *closure = (CountdownClosure *)data;
char buf[64];
int finished = 0;
if ( closure->value++ == 0 || closure->value % 1000 == 0 ) {
sprintf(buf, "Счётчик: %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) {
PtWidget_t *parent = (PtWidget_t *)client;
WorkDialog *dialog;
PtBlockedList_t * blocked_list;
void * response;
w = w; call = call;
dialog = create_working_dialog(parent);
if (dialog) {
CountdownClosure *closure = (CountdownClosure *) malloc(sizeof(CountdownClosure));
if (closure) {
PtWorkProcId_t *id;
closure->dialog = dialog;
closure->value = 0;
closure->maxvalue = 200000;
closure->done = 0;
closure->work_id = id = PtAppAddWorkProc(NULL, count_cb, closure);
PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE, done, closure);
PtRealizeWidget(dialog->widget);
/* Новое: блокируем все окна, кроме диалога, обрабатываем события вплоть до его закрытия,
и затем разблокируем все окна */
blocked_list = PtBlockAllWindows (dialog->widget,
Ph_CURSOR_NOINPUT, Pg_TRANSPARENT);
response = PtModalBlock( &(closure->modal_control), 0 );
printf ("Достигнутое значение равно %d\n", *(int *)response );
free (closure);
PtUnblockWindows (blocked_list);
}
}
return (Pt_CONTINUE);
} // push_button_cb()
int main(int argc, char *argv[]) {
PhDim_t dim;
PtArg_t args[3];
int n;
PtWidget_t *window;
PtCallback_t callbacks[] = {{push_button_cb, NULL}};
char Helvetica14b[MAX_FONT_TAG];
if (PtInit(NULL) = = -1) PtExit(EXIT_FAILURE);
dim.w = 200;
dim.h = 100;
PtSetArg(&args[0], Pt_ARG_DIM, &dim, 0);
if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 1, args)) = = NULL) PtExit(EXIT_FAILURE);
callbacks[0].data = window;
n = 0;
PtSetArg(&args[n++], Pt_ARG_TEXT_STRING, "Count Down...", 0);
/* Используется 14-пунктовый жирный шрифт Helvetica, если он доступен */
if(PfGenerateFontName("Helvetica", PF_STYLE_BOLD, 14, Helvetica14b) = = NULL) {
perror("Невозможно сгенерировать имя шрифта");
}
else {
PtSetArg(&args[n++], Pt_ARG_TEXT_FONT, Helvetica14b, 0);
}
PtSetArg(&args[n++], Pt_CB_ACTIVATE, callbacks, sizeof(callbacks)/sizeof(PtCallback_t));
PtCreateWidget(PtButton, window, n, args);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
} // main()
Если Ваш диалог является автономным и Вам просто надо подождать его завершения, Вам может пригодиться такая функция:
ApModalWait() Обрабатывать события Photon'а, пока заданный виджет не будет уничтожен.
Глава 25. Программирование в Photon'е без PhAB'а
Мы настоятельно рекомендуем при разработке приложений Photon'а пользоваться PhAB'ом – а эта глава для тех чудаков, которые упорно не используют PhAB.
В этой главе обсуждается следующее:
-
Основные шаги
-
Компилирование и линковка не PhAB'овского приложения
-
Образец приложения
-
Увязывание кода приложения с виджетами
-
Полный пример приложения
Основные шаги
Все приложения, использующие библиотеку виджетов Photon'а, выполняют одну и ту же базовую последовательность действий:
-
Включить файл <Pt.h> – стандартный заголовочный файл для библиотеки виджета.
-
Инициализировать инструментальные средства виджетов Photon'а, вызвав функцию PtInit() (или PtAppInit(), которае также создаёт основное окно).
-
Создать виджеты, поддерживающие интерфейс с пользователем, вызвав функцию PtCreateWidget. Эта функция может создавать новые виджеты в потомках заданного виджета или текущего контейнера, или виджеты, не имеющиее родителя.
-
Зарегистрировать какие-либо ответные ракции в приложении с соответствующими виджетами, используя функции PtAddCallback() или PtAddCallbacks().
-
Реализовать виджеты, вызвав функцию PtRealizeWidget(). Эту функцию необходимо вызывать в приложении только один раз. Шаг реализации в действительности создаёт некие регионы Photon'а, которые затем назначаются и отображаются на экран. Пока этот шаг не отработан, никаких регионов не существует, и на экране ничего не отображается.
-
Обрабатывать события Photon'а, вызвав функцию PtMainLoop(). На этом шаге инструментальные средства виджетов Photon'а берут на себя управление приложением и виджетами. Если какие-либо виджеты вызывают функции Вашего приложения, они должны быть предварительно зарегистрированы как ответные реакции.
Компилирование и линковка не PhAB'овского приложения
Чтобы скомпилировать и запустить на исполнение приложение, которое использует библиотеку виджетов Photon'а, Вы должны подлинковаться к главной библиотеке Photon'а ph и к библиотеке отображения phrender. Существует статическая и совместно используемая версии этих библиотек.
| Библиотека photon предназначена только для приложений, созданных в версии 1.14 микроGUI Photon'а. Не комбинируйте эту библиотеку с текущими библиотеками или заголовочными файлами, в противном случае Ваше приложение будет исполняться неверно. |
Мы рекомендуем, чтобы Вы всегда подлинковывались к библиотеке совместного доступа. Это позволит Вам иметь приложение меньшим по размеру и позволит ему наследовать новые возможности, добавляемые к библиотеке виджетов при инсталяции новых реализаций библиотеки совместного доступа.
Библиотека Photon'а включает часть функций и определений виджетов. Если Ваше приложение использует функции Al (переводные) или Px (расширенные), Вам также понадобится подлинковаться к библиотеке phexlib. Если ваше приложение использует функции Ap (PhAB'овские), Вам также надо подлинковаться к библитеке Ар. Имена статических и совместно используемых библиотек одни и те же. По умолчанию qcc линкуется с библиотеками совместного доступа; чтобы линковаться со статическими библиотеками, задайте для qcc опцию – Bstatic.
Например, если у Вас есть приложение по имени hello.c, командой на компилирование и линковку с библиотекой совместного доступа является:
qcc -o hello hello.c -lph -lphrender
Чтобы подлинковать статические библиотеки, команда должна быть такой:
qcc -o hello hello.c -Bstatic -lph -lphrender
Совместно используемая библиотека ph не включает ничего, что требует операций с плавающей запятой (в текущей версии именно виджет PtNumericFloat), в то время как статическая библиотека включает. Чтобы слинковать приложение, включающее виджет PtNumericFloat, Вы можете линковать его только со статической библиотекой или сделать так:
qcc -o hello hello.c lph -Bstatic -lph -Bdynamic -lphrender
Образец приложения
Следующий пример демонстрирует простейшее приложение, использующее библиотеку виджетов. Программа создаёт окно, содержащее одну кнопку.
/* File: hello.c */
#include <Pt.h>
int main( int argc, char *argv[] ) {
PtWidget_t *window;
PtArg_t args[1];
if (PtInit(NULL) = = -1) PtExit(EXIT_FAILURE);
window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 0, NULL);
PtSetArg(&args[0], Pt_ARG_TEXT_STRING, "Нажмите для выхода", 0);
PtCreateWidget(PtButton, window, 1, args);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
}
Что происходит
Хотя это и простое приложение, в каждом из этих вызовов выполняется масса работы.
PtInit()
Функция PtInit() вызывает функцию PhAttach(), чтобы прикрепить к серверу Photon'а канал, и затем вызывает библиотеки виджета.
PtCreateWidget() – первый вызов
Первый вызов функции PtCreateWidget() создаёт оконный виджет, который взаимодействует с оконным менеджером и служит родителем остальным виджетам, созданным в приложении. Аргументами этой функции являются:
-
класс создаваемого виджета (в нашем случае PtWindow)
-
родитель виджета (Pt_NO_PARENT, поскольку окно не имеет родителя)
-
количество элементов в списке аргументов
-
список аргументов начальных значений для ресурсов виджета
Функция PtCreateWidget() возвращает указатель на созданный виджет. Более подробную информацию см. в разделе "Создание виджетов" в главе "Управление виджетами из программного кода приложения". Более полную информацию о виджетах и их ресурсах см. в "Справочнике виджетов Photon'а".
PtSetArg()
Макрос PtSetArg() устанавливает список аргументов, используемый для инициализации ресурсов кнопки при её создании. Более подробно см. в главе "Управление ресурсами в программном коде приложения".
PtCreateWidget() – второй вызов
Все виджеты приложения – кроме окна верхнего уровня – имеют контейнерный виджет в качестве родителя. Контейнерные виджеты могут содержать в себе другие контейнеры. Создание виджетов в приложении приводит к возникновению иерархии, называемой семейством виджетов.
Второй вызов функции PtCreateWidget() создаёт виджет кнопки как потомка оконного виджета, используя для инициализации ресурсов кнопки список аргументов. Вы можете передать в качестве родителя Pt_DEFAULT_PARENT, чтобы сделать виджет потомком контейнерного виджета, созданного самым последним; в этом случае результат будет тем же самым.
PtRealizeWidget()
Функция PtRealizeWidget() отображает виджет и всех его потомков в семействе виджетов. Наше простое приложение вызывает PtRealizeWidget() для окна верхнего уровня, так что отображаются все виджеты приложения.