Страница: 1 | 2 | 3 |
|
Вопрос: Тактика построения программы с GUI - ?
|
Добавлено: 04.04.06 16:33
|
|
Автор вопроса: XPEHOMETP
|
Я вообще-то всегда на Фортране консольные программы писал, для обработки данных, но вот понадобилось свалять нечто интерактивное, чтобы юзер выбирал диапазоны обработки. Решил попробовать PowerBasic. И вот в процессе вдруг до меня доперло, что есть очень важное различие между консольной и гуевой (с GUI) программой. Если программа вывела окошко, то дальше выполняется только то, что записано в Callback, и то, что прописано дальше в теле проги стопорится, пока окошко не закроешь. Или это не так? В общем, я в консольной проге мог нормально выстроить ход вычислений по порядку, в основной программе, а тут получается, что как только я вывел окошко, оно все стопорит, развешивает уши и начинает слушать, какие там сообщения ему винда посылает. И чтобы запустить еще какую-то стадию обработки данных (по нажатии кнопочки на форме), я должен это все запихать в Callback-функцию этой кнопки. А еще какие-то действия - на другую кнопку повесить, к примеру. И тогда у меня весь процесс обработки расползается на куски, раскиданные по разным Callback-функциям разных контролов, которые эту обработку должны запускать. Окошечки я не руками пишу, а пользуюсь PBForms, для удобства. А PBForms вывод каждого окна выделяет в подпрограмму, в этой подпрограмме определяется Callback-функция, в общем, цепочка такая. Чтобы расписать некие действия над данными, лучше их упрятать в еще одну подпрограмму, вызываемую из Callback. И вот для обработки данных тогда придется ВСЕ ДАННЫЕ прописывать как GLOBAL, чтобы их видно было из процедуры на самом конце этой цепочки. Что не хорошо. Либо передавать опять же все данные как параметры по всей этой цепочке (что просто клинический бред).
Вот нет ли такого способа деактивировать выведенное окошко, чтобы оно позволило выполнить некую порцию вычислений, прописанных в основной программе после объявления окна, а потом окошко снова "оживить"? Тогда никаких данных зазря передавать не надо, логика обработки данных будет легко отслеживаться по тексту основного модуля, и вообще все будет путем. Ну, всегда можно убить окошко, повычислять что-нибудь, а потом вызвать окно снова, но это слишком грубый способ. Нет ли чего получше?
Ответить
|
Номер ответа: 2 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #2
|
Добавлено: 04.04.06 19:42
|
Если действия юзера простые, и ограничиваются ответами "да", "нет", "отмена" и т.п. - можно вообще никаких окон не делать, а реакцию юзера получать через MessageBox
Ответить
|
Номер ответа: 3 Автор ответа: XPEHOMETP
Вопросов: 3 Ответов: 30
|
Профиль | | #3
|
Добавлено: 04.04.06 23:05
|
Да не, программы были не с одной подпрограммой. Просто я тут с самого начала пускал маленькие окошки для сбора предварительной инфы: ввести количество точек, площадь электрода и т.п., а потом их убивал. Тут все нормально. А потом должна пойти собственно обработка данных: чтение данных из нескольких файлов в цикле, и здесь я намылился навертеть крутое окошко с кучей контролов и отображением графика (хочу присобачить DPlotJr, кто-нибудь пробовал?) и прочими прибамбасами. И вот тут я и задумался над данным вопросом. Поскольку идет цикл по разным файлам, нужно или весь цикл вообще забить в callback, после показа этого крутого окошка, или запустить цикл, сделать предварительные вычисления, потом показать окно. Но тогда его придется убивать в конце цикла и для каждого файла данных рисовать заново. Если убирать цикл в callback, то придется объявлять глобальными переменными вообще всё: число файлов, массив с именами файлов, массивы с исходными данными, с данными после вычитания фоновых токов, со сглаженными данными, со всеми промежуточными данными вплоть до коэффициентов разложения Фурье (для сглаживания - вырезать наводку 50 Гц). Это гроб. Пока думаю убивать окошко и перерисовывать.
Ответить
|
Номер ответа: 4 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #4
|
Добавлено: 04.04.06 23:26
|
Для этого существует такое понятие, как потоки.
Запускаешь свои вычисления (подпрограмму) отдельным потоком. Он обрабатывает данные и текущее положение дел сохраняет в глобальные переменные (например, процент выполнения, или др. данные, которые необходимо отображать в процессе вычислений). Одновременно с этим потоком запускаешь другой поток, который будет периодически проверять глобальные переменные, изменяемые первым потоком и будет выводить в окошко эту информацию (например рисовать на нём графики).
Поток - это та же подпрограмма, только вызывается она не через Call, а при помощи апи CreateThread. У него есть такое преимущество перед call: если подпрорамму запускать через call, то пока она не закончится (подпрограмма), остальной код выполняться не будет, т.е. ты не сможешь динамически отслеживать изменения, производимые подпрограммой расчётов. При запуске же потока программа не будет ждать завершения подпрограммы, т.е поток будет выполняться параллельно с остальными действиями. В числе этих остальных действий можно выводить информацию в окошко. И убивать его не надо.
Если убивать - это будет очень некрасиво - постоянно мигание окна при удалении/создании. Кроме того ты не сможешь пересоздать окно, пока не завершится подпрограмма расчётов, если она была запущена не отдельным потоком.
Ответить
|
Номер ответа: 7 Автор ответа: XPEHOMETP
Вопросов: 3 Ответов: 30
|
Профиль | | #7
|
Добавлено: 06.04.06 17:18
|
В общем, занимался я POFFигиSмом, пока мозги на бок не съехали, и понял одну вещь: граждане из PowerBasic'a настойчиво советуют использовать стандартную функцию PowerBasic Thread Create вместо функции WinAPI CreateThread. Будем считать, что у них есть для этого веские основания. Общий план получается таков: создается поток, в котором выводится форма, и этот поток изначально замораживается через THREAD CREATE + SUSPEND. И эту форму изначально лучше сделать пустой - в смысле, все надписи = " ". Далее, в основной программе запускается цикл обработки данных по масиву имен файлов, делаются предварительные расчеты, делается картинка с графиком через DPlotJr (если удастся прикрутить) и получается на нее handle. Поток с формой размораживается, а туда изначально можно передать переменную - указатель на структуру с хэндлом на картинку и на строки-надписи для LABEL (а вот сами данные передавать уже не надо!). Это все кидается на форму, юзеру это все нравится или не нравится, он нажимает на кнопочку, поток с формой опять замораживается, а в зависимости от того, что нажал юзер, выполняются те или иные вычисления, строится новый график и все снова передается в отмороженную форму. И все будет очень классно, поскольку поток с формой получает только элементы оформления: надписи и рисунок, данные ему не нужны, и ему абсолютно пофиг, какой там файл обрабатывается. Когда цикл заканчивается, поток убивается вместе с формой, и все путем.
Что осталось непонятным:
1. Как сделать так, чтобы поток с формой сразу же после размораживания полез по переданному ему адресу в структуру, посмотрел там handle на картинку и надписи для LABEL, и кинул это на форму, но чтобы потом с этой целью он туда не лазал, пока его снова не разморозят?
2. Поток с формой самостийно кончиться не может, если только юзер форму не прибьет. А основной поток - запросто. Как притормозить основной поток, пока активен поток с формой? Как я понял, SUSPEND действует только на потоки, созданные через Thread Create, на основную часть программы это не подействует.
3. Через какие функции идет передача сообщений между потоками? Как основной поток узнает, что поток с формой выполнил свою часть задачи и погрузился в спячку?
4. И еще вопросик, к потокам не относящийся. В Delphi есть возможность показать и скрыть, например, кнопочку Button через Button.Show и Button.Hide . Можно ли это сделать в PowerBasic?
Ответить
|
Номер ответа: 8 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #8
|
Добавлено: 06.04.06 17:56
|
Зачем нужно замораживать форму?
Пусть она продолжает быть активной, просто заблокируй кнопки, чтобы юзер не давил на них раньше времени (через EnableWindow).
Сделай глобальную переменную. Типа lngDataAddress = 0. Поток вычислений, закончив предварительную работу пусть занесёт в неё адрес структуры с готовыми данными.
И сделай окошку таймер. По сигналам от таймера проверяй состояние lngDataAddress, и пока данные не готовы в lngDataAddress будет ноль, как только lngDataAddress не равно нулю - значит поток вычислений подготовил данные, и передал их адрес. Можешь отображать эти результаты и разблокировать кнопки (опять же EnableWindow).
Между потоками обычно общение через Events или глобальные переменные. В твоем случае заморачиваться на эвенты смысла нет.
Ответить
|
Номер ответа: 13 Автор ответа: HOOLIGAN
Вопросов: 0 Ответов: 1066
|
Профиль | | #13
|
Добавлено: 09.04.06 19:56
|
Как раз с таймером может прикол выйти,
если один поток(рабочий) будет писать в глобальную переменную, а в это время интерфейсный поток из нее читать?
Потоки не выполняются одновременно. Процессорное время поочередно переключается между потоками, поэтому одновременного чтения/записи быть не может.
Ответить
|
Страница: 1 | 2 | 3 |
Поиск по форуму