Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Power Basic

Страница: 1 | 2 | 3 |

 

  Вопрос: Тактика построения программы с GUI - ? Добавлено: 04.04.06 16:33  

Автор вопроса:  XPEHOMETP
Я вообще-то всегда на Фортране консольные программы писал, для обработки данных, но вот понадобилось свалять нечто интерактивное, чтобы юзер выбирал диапазоны обработки. Решил попробовать PowerBasic. И вот в процессе вдруг до меня доперло, что есть очень важное различие между консольной и гуевой (с GUI) программой. Если программа вывела окошко, то дальше выполняется только то, что записано в Callback, и то, что прописано дальше в теле проги стопорится, пока окошко не закроешь. Или это не так? В общем, я в консольной проге мог нормально выстроить ход вычислений по порядку, в основной программе, а тут получается, что как только я вывел окошко, оно все стопорит, развешивает уши и начинает слушать, какие там сообщения ему винда посылает. И чтобы запустить еще какую-то стадию обработки данных (по нажатии кнопочки на форме), я должен это все запихать в Callback-функцию этой кнопки. А еще какие-то действия - на другую кнопку повесить, к примеру. И тогда у меня весь процесс обработки расползается на куски, раскиданные по разным Callback-функциям разных контролов, которые эту обработку должны запускать. Окошечки я не руками пишу, а пользуюсь PBForms, для удобства. А PBForms вывод каждого окна выделяет в подпрограмму, в этой подпрограмме определяется Callback-функция, в общем, цепочка такая. Чтобы расписать некие действия над данными, лучше их упрятать в еще одну подпрограмму, вызываемую из Callback. И вот для обработки данных тогда придется ВСЕ ДАННЫЕ прописывать как GLOBAL, чтобы их видно было из процедуры на самом конце этой цепочки. Что не хорошо. Либо передавать опять же все данные как параметры по всей этой цепочке (что просто клинический бред).

Вот нет ли такого способа деактивировать выведенное окошко, чтобы оно позволило выполнить некую порцию вычислений, прописанных в основной программе после объявления окна, а потом окошко снова "оживить"? Тогда никаких данных зазря передавать не надо, логика обработки данных будет легко отслеживаться по тексту основного модуля, и вообще все будет путем. Ну, всегда можно убить окошко, повычислять что-нибудь, а потом вызвать окно снова, но это слишком грубый способ. Нет ли чего получше?

Ответить

  Ответы Всего ответов: 36  

Номер ответа: 1
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #1 Добавлено: 04.04.06 17:55
Можешь выполнить все свои операции до вызова PBmain и создания окна, никто не запрещает этого.
Когда предварительные действия/вычисления будут закончены - вызывай PBMain и показывай окно.

Если после показа окна нужно опять вычислять что-то, в зависимости от действий юзера - снова что-то сделать - вызывай код, который будет это "что-то" делать из callback.

Естественно, что реакцию юзера нужно доставить в код, который должен тем или иным способом реагировать на его действия. И кроме как глобальные переменные или передача через входные параметры подпрограмм, других способов доставить реакцию юзера до кода нет.

И какая необходимость деактивировать окно? Если ты из callback вызвал подпрограмму, окно и так не будет ни на что реагировать, пока подпрограмма не будет закончена. А когда подпрограмма закончит свою работу - окно само "оживет" и будет готово к приёму следующей реакции юзера.

P.S.
Похоже, все твои программы до сих пор состояли из одной подпрограммы :)

Ответить

Номер ответа: 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, то пока она не закончится (подпрограмма), остальной код выполняться не будет, т.е. ты не сможешь динамически отслеживать изменения, производимые подпрограммой расчётов. При запуске же потока программа не будет ждать завершения подпрограммы, т.е поток будет выполняться параллельно с остальными действиями. В числе этих остальных действий можно выводить информацию в окошко. И убивать его не надо.

Если убивать - это будет очень некрасиво - постоянно мигание окна при удалении/создании. Кроме того ты не сможешь пересоздать окно, пока не завершится подпрограмма расчётов, если она была запущена не отдельным потоком.

Ответить

Номер ответа: 5
Автор ответа:
 Neco



ICQ: 247906854 

Вопросов: 133
Ответов: 882
 Web-сайт: neco.pisem.net
 Профиль | | #5
Добавлено: 05.04.06 06:03
XPEHOMETP
Имхо, ты всё ещё по-консольному мыслишь. 8)
А зачем тебе вообще помещать цикл в callback? Callback он нужен только для реагирования окна на внешние раздражители (самый вредный из них - юзер). Потоки это, конечно, выход, но стоит ли их здесь привлекать? Почему нельзя сделать:
1. Подготовка (сбор данных и т.п.)
2. Создание окна прогресса + ShowDialog (или как там его)
3. Цикл обработки с SendMessage окну для обновления (только не очень часто, чтоб не мерцало).

Насколько мне помнится я делал кряк в PB и для понтов кидал туда прогресс-бар. Так вот прогресс у меня заполнялся в цикле For Next.

Ответить

Номер ответа: 6
Автор ответа:
 XPEHOMETP



Вопросов: 3
Ответов: 30
 Профиль | | #6 Добавлено: 05.04.06 13:35
Ну да, консольное у меня мышление, согласен. Похоже, слишком круто взял для начала. Идея на счет потоков мне понравилась, только, наверно, сложно это. Ладно, будем пробовать!

Ответить

Номер ответа: 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 или глобальные переменные. В твоем случае заморачиваться на эвенты смысла нет.

Ответить

Номер ответа: 9
Автор ответа:
 XPEHOMETP



Вопросов: 3
Ответов: 30
 Профиль | | #9 Добавлено: 06.04.06 19:26
Ладно, спасибо, буду дальше разбираться.

Ответить

Номер ответа: 10
Автор ответа:
 silently_fox



ICQ: 257821932 

Вопросов: 5
Ответов: 9
 Профиль | | #10 Добавлено: 08.04.06 01:59
Зачем пользоваться таймером если можно послать окну
сообщение (%WM_USER xxx, а еще лучше WM_MESS=RegisterWindowMessage("WM_MESS";)).

Да и использование API CreateThread не просто так считается плохим стилем:
будет какая же ситуация как с Вижл Бейсиком, а именно половина встроиных
функций не будет работать, так же как и некоторые глобальные переменные.
В PB так же как и в Си, функции в RTL могут стабильно работать в мультитредовой среде
но эти функции должны знать где они выполняются поэтому и вызывать для создания треды
нужно create thread, для того чтобы дать знать что мы создаем поток.

Ответить

Номер ответа: 11
Автор ответа:
 XPEHOMETP



Вопросов: 3
Ответов: 30
 Профиль | | #11 Добавлено: 08.04.06 15:44
С таймером всяко проще. Мне так понятнее, меньше ошибок насажаю.

Ответить

Номер ответа: 12
Автор ответа:
 silently_fox



ICQ: 257821932 

Вопросов: 5
Ответов: 9
 Профиль | | #12 Добавлено: 09.04.06 18:48
Как раз с таймером может прикол выйти,
если один поток(рабочий) будет писать в глобальную переменную, а в это время интерфейсный поток из нее читать?
да и вообще использовать глобальные переменные в многопоточной среде без синхронизации не правильно.
если нужны переменные глобальные только в конкретном потоке используй для их обьявления threaded(PB for Win8.0 или PBCC 4.0)

Ответить

Номер ответа: 13
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #13 Добавлено: 09.04.06 19:56
Как раз с таймером может прикол выйти,
если один поток(рабочий) будет писать в глобальную переменную, а в это время интерфейсный поток из нее читать?


Потоки не выполняются одновременно. Процессорное время поочередно переключается между потоками, поэтому одновременного чтения/записи быть не может.

Ответить

Номер ответа: 14
Автор ответа:
 Sharp


Лидер форума

ICQ: 216865379 

Вопросов: 106
Ответов: 9979
 Web-сайт: sharpc.livejournal.com
 Профиль | | #14
Добавлено: 09.04.06 22:13
А если это строка?

Ответить

Номер ответа: 15
Автор ответа:
 XPEHOMETP



Вопросов: 3
Ответов: 30
 Профиль | | #15 Добавлено: 09.04.06 23:18
Вот я и собирался поток с формой замораживать. И буду его замораживать, поскольку вычисления в основном потоке пойдут довольно громоздкие, преобразование Фурье, на слабом компе довольно долго считается. Нечего в это время таймер зазря гонять.

Ответить

Страница: 1 | 2 | 3 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам