Запуск независимого графического окна

Я новичок в программировании с графическим интерфейсом, и я пытаюсь написать графическую библиотеку на D для использования с некоторыми консольными научными приложениями. Я использую DFL в качестве моей библиотеки графического интерфейса.

Предположим, что в моей форме графика есть метод с именем showPlot(), который должен отображать график на экране. Я хотел бы, чтобы любой поток в моем приложении открывал окно графика и либо блокировался до тех пор, пока график не будет закрыт, либо продолжит работу, при этом вызывающему showPlot() не нужно знать, что любой другой поток делает в отношении графика, или какие сюжеты были созданы в прошлом и все еще могут быть на экране. (Конечно, внутренние органы showPlot() могут обладать этим знанием.)

Я все еще пытаюсь понять, как библиотеки с графическим интерфейсом обычно работают под капотом. Похоже, у вас должен быть только один поток графического интерфейса и одна основная форма. Я был бы признателен за ответы на уровне шаблонов проектирования, не зависящих от языка/библиотеки, в дополнение к тем, которые зависят от языка/библиотеки.

Редактировать: Чтобы подчеркнуть, это приложение не имеет графического интерфейса, кроме сюжетов, которые оно выдает в интересных моментах своего исполнения. По сути это консольное приложение плюс несколько сюжетов. Следовательно, четко определенной «основной» формы не существует.


person dsimcha    schedule 08.07.2010    source источник


Ответы (2)


То, что вы сможете сделать, скорее всего, будет зависеть от того, как работает DFL. Как правило, в приложении с графическим интерфейсом есть поток событий, который обрабатывает все события в вашем приложении — будь то события перерисовки, события нажатия кнопки, события щелчка мыши или что-то еще. Этот поток вызывает обработчик событий, который зарегистрирован для обработки события (часто это виджет, на который нажали, или что-то еще). Проблема, с которой вы сталкиваетесь, заключается в том, что если вы попытаетесь сделать слишком много в этих обработчиках событий, вы заблокируете поток событий, поэтому другие события (включая события перерисовки) не обрабатываются своевременно. Некоторые инструменты GUI даже специально ограничивают то, что вам разрешено делать в обработчике событий. Некоторые также ограничивают определенные типы операций этим конкретным потоком (например, выполнение чего-либо — особенно создание объектов — с фактическим кодом GUI, таким как различные виджеты или классы окон, которые обязательно должны быть в наборе инструментов).

Как правило, способ справиться с этим состоит в том, чтобы поток событий либо запускал отдельные потоки в обработчиках событий и позволял этим другим потокам фактически обрабатывать события, либо вы задавали некоторое количество состояний в обработчике событий, отдельном, уже запущенном поток предупреждается об этом изменении состояния (возможно, с использованием шаблона наблюдателя) и обрабатывает вещи соответствующим образом на основе этого состояния. В любом случае то, что делают сами обработчики событий, обычно довольно ограничено.

Как работают графические интерфейсы, как правило, очень основаны на событиях. Программа обрабатывает события, поступающие от пользователя и системы, и почти ничего не делает без получения сигнала об этом. Часто приложения с графическим интерфейсом не делают ничего, пока им не сообщит об этом событие (хотя во многих случаях фоновый поток выполняет какую-то работу отдельно от событий). То, что вы пытаетесь сделать, не выглядит особенно основанным на событиях, так что это немного усложняет ситуацию.

Я предполагаю, что вам нужно будет, чтобы ваше приложение создавало новое окно каждый раз, когда вы хотите создать новый сюжет. Это окно, вероятно, будет дочерним окном главного окна, которое будет скрыто, поскольку оно вам явно не нужно, и, предположительно, DFL требует, чтобы у вас было какое-то главное окно. Существует неплохая вероятность того, что каждый поток, который хотел создать окно, должен будет сообщить об этом главному окну графического интерфейса, но на самом деле это зависит от DFL. Также возможно, что DFL позволяет любому потоку создавать новые элементы графического интерфейса (например, новое окно).

В любом случае, это почти наверняка будет основной поток событий, который фактически обрабатывает окно. Поток, который хочет создать окно и заполнить его, вероятно, должен будет создать окно (прямо или косвенно), а затем обновить состояние окна, обновив набор общих переменных, чтобы поток событий мог соответствующим образом перерисовать окно. . Максимум, что первоначальный поток мог бы сделать, фактически перерисовать окно, это отправить событие перерисовки окну после того, как он обновил состояние общих переменных, содержащих данные, необходимые для рисования окна. Он не справился бы с самой картиной.

Что касается блокировки потока, то, скорее всего, ему придется ждать, пока событие закрытия окна не будет получено потоком событий, и какая-то общая переменная не будет обновлена, или вы можете заставить его перейти в спящий режим, пока поток событий не разбудит его после получение события закрытия окна. В случае, когда вы не хотите, чтобы он блокировался, он просто не будет ждать, пока ему сообщат о событии закрытия окна, и будет продолжать пыхтеть.

В любом случае, надеюсь, этой информации достаточно, чтобы направить вас в правильном направлении. Обычно события управляют всем, и все, так сказать, зависает от графического интерфейса. Но в вашем случае ваше приложение использует графический интерфейс, больше похожий на стандартный вывод. Таким образом, вам лучше сначала поработать над некоторыми небольшими, довольно глупыми приложениями с графическим интерфейсом, которые являются реальными, нормальными, основанными на событиях приложениями с графическим интерфейсом (например, игра в крестики-нолики или что-то в этом роде), чтобы получить лучшее представление о том, как Материал GUI работает, прежде чем пытаться заставить его работать несколько менее стандартным образом, как вы.

person Jonathan M Davis    schedule 08.07.2010
comment
Аналогия с stdout довольно хороша — я бы хотел, чтобы система с графическим интерфейсом вела себя именно так в данном случае. - person dsimcha; 08.07.2010

Платформы графического интерфейса обычно имеют собственный цикл событий и вызывают код приложения как обратные вызовы в ответ на внешние события (нажатия кнопок, таймеры, перерисовка и т. д.). Основное различие между «типичным» консольным приложением и приложением с графическим интерфейсом заключается в том, что вы отказываетесь от контроля над тем, когда функция вызывается в среде графического интерфейса.

Потоки обычно используются, когда код приложения содержит какой-то длительный процесс, который нельзя прервать на более мелкие фрагменты (большие вычисления, копирование файлов, получение контроля над миром и т. д.). Затем один поток используется для поддержки отклика графического интерфейса, а работа выполняется в отдельном потоке. Основная проблема заключается в правильной синхронизации обоих потоков, поскольку большинство инструментов GUI не способны обрабатывать вызовы из более чем одного потока. Когда у вас есть только небольшие рабочие части, которые не блокируют GUI на долгое время (‹0,1 с), то лучше остаться без рабочих потоков.

Я также настоятельно рекомендую отделить код GUI и логику приложения друг от друга. Смешивание кода GUI и кода приложения — это кошмар обслуживания.

person Rudi    schedule 08.07.2010
comment
Я думаю, вы упустили суть. Дело в том, что это не типичное приложение с графическим интерфейсом. В основном это консольное приложение для научных вычислений, но оно выдает несколько сюжетов во время интересных частей своего исполнения. В этом случае кроме этих графиков нет графического интерфейса. - person dsimcha; 08.07.2010