Gui — На чем сделать GUI приложение


Содержание

На чем писать GUI?

01.06.2020, 21:29

На чем лучше писать программы на Си
Сейчас я пишу программы на Visual Studio 2012 и понимаю что это смахивает на извращение,потому что.

Где писать код под Си и чем компилировать?
Здравствуйте! Начал изучать Си. Где писать код?(Читал, что можно даже в блокноте) И чем.

Где и в на чем легче писать распределенные приложения ?
Где и в на чем легче писать распределенные приложения ?

На чем писать GUI?
Всем привет! Ребят, помогите пожалуйста в выборе IDE для написания гуя с базой данных. Не могу.

На чём писать GUI
Нужен какая-нибудь не громоздкая, чтобы программа с пустым окном была до 1 мб со статической.

Начинаем программировать на Qt

Добро пожаловать в мир Qt — кроссплаформенного инструмента для создания графических интерфейсов. В этом руководстве мы изучим базовые аспекты работы с Qt, на примере написания простого текстового редактора. После прочтения этого руководства, вы сможете углубиться в изучение документации API и искать другую необходимую информацию для разработки приложений.

Hello Notepad

В первом примере, мы создадим простое окно для редактирования текста. Это самая элементарная программа с графическим интерфейсом Qt.

Исходный код примера:

Рассмотрим подробно каждую строчку кода. В первых двух, мы подключаем заголовочные файлы классов QApplication и QTextEdit, которые необходимы для работы этого примера. Каждому классу в Qt соответствует заголовочный файл с таким же названием.

В строке 6 создается объект класса QApplication, который управляет основными ресурсами, необходимыми для запуска любой программы с графическим интерфейсом Qt. Ему необходимо передать аргументы argv и args функции main() , т.к. Qt использует некоторые параметры командной строки, передаваемые при запуске программы.

В восьмой строке кода создается объект класса QTextEdit. QTextEdit — это визуальный элемент графического интерфейса. В Qt используются определенные виджеты — например, полосы прокрутки, метки и кнопки radio. Виджет может быть контейнером для хранения других виджетов. Наглядным примером является главное диалоговое окно программы.

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

В строке 11, создается объект QApplication, который генерирует цикл событий в процессе работы приложения. События генерируются и передаются на обработку виджетам. Примерами событий могут являться клики мыши, нажатия клавиш на клавиатуре и т.п. Когда вы вводите текст в окне редактирования виджета QTextEdit, нажатия клавиш обрабатываются средствами виджета, и вводимый текст отображается в процессе набора.

Для запуска программы, откройте командную строку и зайдите в директорию с .cpp файлом программы. Выполните следующие команды shell-интерпретатора, чтобы скомпилировать пример.

После успешного выполнения предыдущих команд, скомпилированная программа будет размещена в директории текущего проекта (в Windows вы можете использовать nmake вместо make . Исполняемые файлы будут размещены в директориях debug или release, которые создадутся командой make . qmake — это утилита, которая создает файлы конфигурации Qt-проекта, если ей передан аргумент -project . После создания файла конфигурации (.pro), qmake генерирует Makefile, который используется утилитой make для сборки приложения. Позже, мы рассмотрим процесс написания собственных .pro файлов.

Добавления кнопки выхода

В реальных проектах, обычно используется более одного виджета. Сегодня мы добавим кнопку QPushButton под полем редактирования текста. При клике на нее, программа будет завершаться.

Рассмотрим исходный код программы.

Сначала подключается заголовочный файл QtGui. В нем содержатся все классы графического интерфейса Qt.

В строке 10 применяется механизм сигналов и слотов, для того, чтобы закрыть программу после нажатия на кнопку выхода. Слот — это функция, которая может быть вызвана в процессе выполнения программы. Сигнал — функция, вызывающая функции-слоты, которые с ним посредством QObject::connect.

quit()слот класса QApplication, завершающий работу приложения. clicked() — сигнал, который передает нажатая кнопка QPushButton. Статическая функция QObject::connect связывает определенный слот и сигнал. SIGNAL() и SLOT() — макросы, которые принимают сигнатуры сигналов и слотов, для их связи между собой. Также, функции connect() необходимо передать указатели на объекты, которые будут принимать и рассылать сигналы.

В строке 12 создается объект класса QVBoxLayout. Как уже было сказано, виджеты могут содержать в себе другие виджеты. Можно задавать координаты и размер вложенных виджетов непосредственно, но обычно для этого используют слои (layouts). Слой управляет границами вложенных виджетов. Например, объект QVBoxLayout размещает виджеты в одну вертикальную колонку.

В строках 13 и 14, мы добавляем поле редактирования текста и кнопку выхода к слою layout . В строке 17 задается главный слой для всего приложения.

Наследование QWidget

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

Рассмотрим следующий код.

Макрос Q_OBJECT объявляет наш класс как QObject. Он должен находиться в самом начале определения класса. QObject добавляет несколько расширенных возможностей к обычному классу C++. Например, имена классов и слотов можно запросить в процессе выполнения. Также, мы можем узнать типы параметров слота и вызвать его.

В строке 13 объявляется слот quit() . В последствии, мы присоединим этот слот к сигналу.

В предыдущих примерах, создание графики и присоединение слотов осуществлялось внутри функции main() . Сейчас мы будем использовать конструктор Notepad.

Как видно из определения класса, мы используем указатели на объекты textEdit и quitButton . Для объектов QObject, почти всегда рациональнее выделять память в куче, вместо их копирования.

Мы используем функцию tr() для обработки всех строк, которые видны пользователю. Эта функция применяется, когда приложение нужно перевести на несколько языков. Здесь мы не будем углубляться в детали, но вы можете найти нужную информацию в описании библиотеки Qt Linguist.

Создание файлов .pro

Ранее, мы использовали команду qmake -project для создания файлов .pro. В следующем примере, мы создадим этот файл вручную.

Теперь для сборки программ на Qt, будут использоваться следующие команды.

Использование QMainWindow

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

Рассмотрим определение класса Notepad .

Мы создали два дополнительных слота — open() и save() . Они будут открывать и сохранять документ. Пример их реализации будет представлен позже.

Очень часто, один и тот же слот используется одновременно несколькими виджетами. Например, в пунктах меню и соответственных кнопках на панели инструментов. Чтобы упростить этот процесс, в Qt существует класс QAction, который можно передавать сразу нескольким виджетам и присоединять к слоту. Например, QMenu и QToolBar могут создавать пункты меню, используя одно действие QAction. Скоро вы убедитесь, как это облегчает процессе разработки.

Сначала, мы используем конструктор класса Notepad для создания графического интерфейса программы.

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

Когда пользователь кликает на пункте меню, рассылается сигнал действия и вызывается соответствующий слот.

Сохранение и открытие

В этом примере, мы реализуем функционал слотов open() и save() , которые были добавлены в предыдущем примере.

Первый шаг — это запрос у пользователя имени файла для открытия. В состав Qt входит класс QFileDialog — диалоговое окно, в котором пользователь может выбрать файл. Статическая функция getOpenFileName() открывает модальное окно для выбора файла и возвращает путь к выбранном файлу. Если пользователь отменил действие, функция возвращает пустую строку.

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

Чтение данных становится тривиальной задачей, благодаря классу QTextStream — оберткой над объектом QFile. Функция readAll() возвращает содержимое файла, как объект QString. Это содержимое мы поместим в поле для редактирования текста. Затем, мы закрываем файл с помощью функции close(), чтобы вернуть файловый дескриптор операционной системе.

Перейдем к реализации слота save() .

Для сохранения данных мы снова используем класс QTextStream. При помощи него, можно записывать строки QString в файл, используя оператор .

На чем сейчас делать GUI?

Под виндой все просто. Есть C# и .Net, работает под все платформы начиная с Windows XP, мое приложение работает под Windows Vista из коробки.

Хочу перенести приложение под Linux. Так как кода немного (приложение графическая утилита), тащить библиотеку mono не вариант.

Можно портировать на C++ или на Java (желательно), но какую взять библиотеку (точно не Qt), чтобы в GTK приложение не выглядело странно?

Производительности C# хватает с головой, плюс есть работа с сетью, поэтому Java тут выглядит предпочтительнее, что под нее есть, из живого кроме JavaFX и Swing. А если ничего нет, то что толкового и живого есть в C++?

Форум русскоязычного сообщества Ubuntu

Хотите сделать посильный вклад в развитие Ubuntu и русскоязычного сообщества?
Помогите нам с документацией!

Автор Тема: Как научится делать GUI приложения ? (Прочитано 1629 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Страница сгенерирована за 0.065 секунд. Запросов: 24.

© 2012 Ubuntu-ru — Русскоязычное сообщество Ubuntu Linux.
© 2012 Canonical Ltd. Ubuntu и Canonical являются зарегистрированными торговыми знаками Canonical Ltd.

Gui — На чем сделать GUI приложение?

В этой теме рассмотрено Средство GUI Builder в среде IDE NetBeans, и приведено пошаговое описание процесса создания графического интерфейса пользователя для приложения с именем MyApplication с помощью конструктора графического интерфейса пользователя IDE NetBeans.

Данная тема обучает следующему:

  • Работать с интерфейсом средства GUI Builder
  • Создавать GUI Container (контейнер GUI)
  • Добавлять компоненты
  • Выравнивать их положение
  • Изменять размер
  • Настраивать привязку компонентов
  • Управлять поведением при автоматическом изменении размера
  • Редактировать свойства компонентов.

Средство GUI Builder в среде IDE позволяет создавать профессиональные графические интерфейсы без наличия знаний о работе с диспетчерами компоновки. Проектирование форм можно выполнять путем простого размещения компонентов формы в требуемых позициях.

Визуальные средства поддержки в конструкторе GUI

Направляющие линии


Направляющие (Таблица 1) появляются только при добавлении или перемещении элементов и указывают предпочтительные позиции, согласно которым размещаются элементы при отпускании кнопки мыши. После размещения направляющие заменяются сплошными линиями, обозначающими выравнивание элементов и точки их привязки.

Таблица 1. Направляющие линии.

Действие Изображение Описание
Отступ Отступы – это предпочтительные расстояния между элементами и контейнерами, в которых расположены эти элементы. Отступы обозначаются горизонтальными и вертикальными штриховыми линиями.
Смещение Смещения – это предпочтительный интервал между смежными элементами. Предлагаемые смещения обозначаются горизонтальными и вертикальными штриховыми линиями.
Базовая линия Выравнивание по базовой линии отражает предпочтительное соотношение между смежными элементами, содержащими отображаемый текст. Базовая линия обозначается штриховой горизонтальной линией.
Граница Границы (верхняя, нижняя, левая и правая) определяют возможные соотношения по выравниванию смежных элементов. Предлагаемые границы обозначаются штриховыми горизонтальными и вертикальными линиями.
Сдвиг Сдвиг определяет, на сколько один элемент смещен относительно другого в направлении вниз и вправо. Предлагаемый сдвиг обозначается двумя вертикальными штриховыми линиями.
Предпочтительное расстояние Предпочтительные расстояния (маленькое, среднее и большое) представляют собой размеры промежутков между смежными компонентами. Предпочтительные расстояния предлагаются в виде горизонтальных или вертикальных штриховых направляющих.

Точки привязки

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

Таблица 2. Точки привязки.

Действие Изображение Описание
Контейнер Привязки, соединяющие отдельные элементы c контейнерами, внутри которых они расположены, представлены в виде небольших полукруглых индикаторов со штриховыми линиями, идущими от границы контейнера непосредственно к элементу.
Элемент Привязки, соединяющие отдельные элементы со смежным элементами, представлены в виде небольших полукруглых индикаторов со штриховыми линиями, идущими от одного элемента до другого.

Установка размеров индикаторов

Таблица 3. Установка размеров индикаторов.

Действие Изображение Описание
Одинаковый размер Если ряд элементов (не обязательно смежных) имеет одинаковую высоту или ширину, говорят, что они имеют одинаковый размер. Эта ситуация обозначается наличием небольших прямоугольников на верхней границе каждого элемента, имеющего это свойство.
Автоматическое изменение размера Автоматическое изменение размера – это динамическое изменение ширины или высоты элемента во время выполнения. Автоматическое изменение размера обозначается состоянием кнопок изменения размера в горизонтальном и вертикальном направлениях (переменность ширины и высоты соответственно) на панели инструментов Конструктора GUI. Для включения функции автоматического изменения размера выберите изменять размерв списке «Другие свойства» окна «Свойства».
Цукерберг рекомендует:  Вакансии РДВ-Медиа

Выделение и ручки изменения размера

Таблица 4. Выделение и ручки изменения размера.

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

Создание приложения с графическим интерфейсом

Видеоурок:

Текстовая версия видеоурока

Создание приложения

  1. Запустите IDE NetBeans.
  2. В меню IDE выберите ‘Файл > Создать проект’, как показано на рисунке (см. рис. 1).

Рис. 1. Создание нового проекта

В мастере создания проекта разверните категорию «Java» и выберите «Приложение Java». Затем нажмите кнопку «Дальше»(рис. 2).

Рис. 2. Выбор приложения Java

  • На странице мастера «Новый Приложение Java» выполните следующие действия (рис. 3):
    • Введите MyApplication в поле «Имя проекта»;
    • Не устанавливайте флажок «Использовать отдельную папку для хранения библиотек»;
    • !Снимите флажок «Создать главный класс».
    • Нажмите кнопку «Готово».

    Рис. 3. Настройка мастера

    Проект будет создан и открыт в среде IDE (рис. 4).

    Рис. 4. Проект MyApplication в среде IDE

    Создание формы приложения

    В примере создана форма JFrame, и в нее добавлена метка JLabel.

    Создание формы JFrame:

    1. В окне ‘Проекты’ разверните узел ImageDisplayApp.
    2. Щелкните правой кнопкой узел «Исходные файлы» и выберите форму «Создать» > «JFrame»(рис. 5)

    Рис. 5. Выбор формы JFrame.

    В качестве имени класса введите MyJFrame. Для этого Введите MyJFrame в поле «Имя класса» (рис. 6)

    Рис. 6. Редактирование формы JFrame в мастере.

    В результате будет создана пустая форма и откроется окно Палитра, если оно было закрыто(рис. 7).

    Рис. 7. Пустая формы JFrame.

      В разделе «Палитра» выберите компонент «Метка» и перетащите его в форму(рис. 8).

    Рис. 8. Выбор и перенос метки на форму.

  • В разделе «Палитра» выберите компонент «Кнопка» и перетащите его в форму.
  • Создание обработчика события кнопки:

    • Двойным нажатием по кнопке, будет создан обработчик события нажатия кнопки.

    Создадим обработчик события нажатия кнопки, для кнопки «jButton1». В нашей программе появится код:

    Пусть по нажатию кнопки, у нас изменится текст метки «jLabel1». Для этого добавим код в обработчик события нажатия кнопки «jButton1»:

    Изменение текста кнопки

    Изменим текст кнопки на «HelloWorld»

    Текст кнопки можно изменить несколькими способами. Например:(рис. 9)

    • Через контекстное меню, вызванное правой кнопкой мыши, когда курсор находится в области кнопки
    • Через окно «Свойства» в строке значения с именем «text»

    Рис. 9. Изменение текста кнопки.

    Запустим проект. Выйдет окно(рис. 10):

    Рис. 10. Выбор главного класса проекта.

    Обучение Python GUI (уроки по Tkinter)

    В этом уроке мы узнаем, как разрабатывать графические пользовательские интерфейсы, с помощью разбора некоторых примеров графического интерфейса Python с использованием библиотеки Tkinter.

    Библиотека Tkinter установлена в Python в качестве стандартного модуля, поэтому нам не нужно устанавливать что-либо для его использования. Tkinter — очень мощная библиотека. Если вы уже установили Python, можете использовать IDLE, который является интегрированной IDE, поставляемой в Python, эта IDE написана с использованием Tkinter. Звучит круто!

    Мы будем использовать Python 3.7 поэтому, если вы все еще используете Python 2.x, настоятельно рекомендуем перейти на Python 3.x, если вы не в курсе нюансов изменения языка, с целью, чтобы вы могли настроить код для запуска без ошибок.

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

    Создание своего первого графического интерфейса

    Для начала, следует импортировать Tkinter и создать окно, в котором мы зададим его название:

    Результат будет выглядеть следующим образом:
    Прекрасно! Наше приложение работает.
    Последняя строка вызывает функцию mainloop . Эта функция вызывает бесконечный цикл окна, поэтому окно будет ждать любого взаимодействия с пользователем, пока не будет закрыто.

    В случае, если вы забудете вызвать функцию mainloop , для пользователя ничего не отобразится.


    Создание виджета Label

    Чтобы добавить текст в наш предыдущий пример, мы создадим lbl , с помощью класса Label , например:

    Затем мы установим позицию в окне с помощью функции grid и укажем ее следующим образом:

    Полный код, будет выглядеть следующим образом:

    GUI на Java

    Пользовательский интерфейс на Java прошел весьма тернистый путь становления и развития. Долгое время его обвиняли в медленной работе, жадности к ресурсам системы, ограниченной функциональности. Появление .NET с более быстрыми графическими компонентами еще больше пошатнуло позиции Java. Но нет худа без добра — все эта движуха только подстегивала разработчиков Java к развитию и улучшению графических библиотек. Посмотрим, что из этого получилось.

    Abstract Window Toolkit

    Хакер #183. Малварь для Android

    AWT была первой попыткой Sun создать графический интерфейс для Java. Они пошли легким путем и просто сделали прослойку на Java, которая вызывает методы из библиотек, написанных на С. Библиотечные методы создают и используют графические компоненты операционной среды. С одной стороны, это хорошо, так как программа на Java похожа на остальные программы в рамках данной ОС. Но с другой стороны, нет никакой гарантии, что различия в размерах компонентов и шрифтах не испортят внешний вид программы при запуске ее на другой платформе. Кроме того, чтобы обеспечить мультиплатформенность, пришлось унифицировать интерфейсы вызовов компонентов, из-за чего их функциональность получилась немного урезанной. Да и набор компонентов получился довольно небольшой. К примеру, в AWT нет таблиц, а в кнопках не поддерживается отображение иконок.

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

    Достоинства:

    • часть JDK;
    • скорость работы;
    • графические компоненты похожи на стандартные.

    Недостатки:

    • использование нативных компонентов налагает ограничения на использование их свойств. Некоторые компоненты могут вообще не работать на «неродных» платформах;
    • некоторые свойства, такие как иконки и всплывающие подсказки, в AWT вообще отсутствуют;
    • стандартных компонентов AWT очень немного, программисту приходится реализовывать много кастомных;
    • программа выглядит по-разному на разных платформах (может быть кривоватой).

    заключение:

    В настоящее время AWT используется крайне редко — в основном в старых проектах и апплетах. Oracle припрятал обучалки и всячески поощряет переход на Swing. Оно и понятно, прямой доступ к компонентам оси может стать серьезной дырой в безопасности.

    Swing

    Как выглядит Swing

    Вслед за AWT Sun разработала набор графических компонентов под названием Swing. Компоненты Swing полностью написаны на Java. Для отрисовки используется 2D, что принесло с собой сразу несколько преимуществ. Набор стандартных компонентов значительно превосходит AWT по разнообразию и функциональности. Стало легко создавать новые компоненты, наследуясь от существующих и рисуя все, что душе угодно. Стала возможной поддержка различных стилей и скинов. Вместе с тем скорость работы первых версий Swing оставляла желать лучшего. Некорректно написанная программа и вовсе могла повесить винду намертво.

    Тем не менее благодаря простоте использования, богатой документации и гибкости компонентов Swing стал, пожалуй, самым популярным графическим фреймворком в Java. На его базе появилось много расширений, таких как SwingX, JGoodies, которые значительно упрощают создание сложных пользовательских интерфейсов. Практически все популярные среды программирования Java включают графические редакторы для Swing-форм. Поэтому разобраться и начать использовать Swing не составит особого труда.

    Достоинства:

    • часть JDK, не нужно ставить дополнительных библиотек;
    • по Swing гораздо больше книжек и ответов на форумах. Все проблемы, особенно у начинающих, гуглу досконально известны;
    • встроенный редактор форм почти во всех средах разработки;
    • на базе свинга есть много расширений типа SwingX;
    • поддержка различных стилей (Look and feel).

    Недостатки:

    • окно с множеством компонентов начинает подтормаживать;
    • работа с менеджерами компоновки может стать настоящим кошмаром в сложных интерфейсах.

    Заключение:

    Swing жил, Swing жив, Swing будет жить. Хотя Oracle и старается продвигать JavaFX, на сегодняшний день Swing остается самым популярным фреймворком для создания пользовательских интерфейсов на Java.

    Standard Widget Toolkit

    SWT был разработан в компании IBM в те времена, когда Swing еще был медленным, и сделано это было в основном для продвижения среды программирования Eclipse. SWT, как и AWT, использует компоненты операционной системы, но для каждой платформы у него созданы свои интерфейсы взаимодействия. Так что для каждой новой системы тебе придется поставлять отдельную JAR-библиотеку с подходящей версией SWT. Это позволило более полно использовать существующие функции компонентов на каждой оси. Недостающие функции и компоненты были реализованы с помощью 2D, как в Swing. У SWT есть много приверженцев, но, положа руку на сердце, нельзя не согласиться, что получилось не так все просто, как хотелось бы. Новичку придется затратить на изучение SWT намного больше времени, чем на знакомство с тем же Swing. Кроме того, SWT возлагает задачу освобождения ресурсов на программиста, в связи с чем ему нужно быть особенно внимательным при написании кода, чтобы случайное исключение не привело к утечкам памяти.

    Достоинства:

    • использует компоненты операционной системы — скорость выше;
    • Eclipse предоставляет визуальный редактор форм;
    • обширная документация и множество примеров;
    • возможно использование AWT- и Swing-компонентов.

    Недостатки:

    • для каждой платформы необходимо поставлять отдельную библиотеку;
    • нужно все время следить за использованием ресурсов и вовремя их освобождать;
    • сложная архитектура, навевающая суицидальные мысли после тщетных попыток реализовать кастомный интерфейс.

    Заключение:

    Видно, что в IBM старались. Но получилось уж очень на любителя…

    JavaFX

    Как выглядит JavaFX

    JavaFX можно без преувеличения назвать прорывом. Для отрисовки используется графический конвейер, что значительно ускоряет работу приложения. Набор встроенных компонентов обширен, есть даже отдельные компоненты для отрисовки графиков. Реализована поддержка мультимедийного контента, множества эффектов отображения, анимации и даже мультитач. Внешний вид всех компонентов можно легко изменить с помощью CSS-стилей. И самое прекрасное — в JavaFX входит набор утилит, которые позволяют сделать родной инсталлятор для самых популярных платформ: exe или msi для Windows, deb или rpm для Linux, dmg для Mac. На сайте Oracle можно найти подробную документацию и огромное количество готовых примеров. Это превращает программирование с JavaFX в легкое и приятное занятие.

    Достоинства:

    • быстрая работа за счет графического конвейера;
    • множество различных компонентов;
    • поддержка стилей;
    • утилиты для создания установщика программы;
    • приложение можно запускать как десктопное и в браузере как часть страницы.

    Недостатки:

    • фреймворк еще разрабатывается, поэтому случаются и падения и некоторые глюки;
    • JavaFX пока не получил широкого распространения.

    Заключение:

    Хорошая работа, Oracle. Фреймворк оставляет только позитивные впечатления. Разобраться несложно, методы и интерфейсы выглядят логичными. Хочется пользоваться снова и снова!

    Визуальные библиотеки на практике

    SWT: погодный виджет

    Для демонстрации возможностей наиболее популярных графических библиотек и основных принципов работы с ними сделаем несколько небольших виджетов с отображением различной информации.

    И начнем, пожалуй, с самого популярного виджета — отображения текущей погоды, для реализации которого выберем SWT.

    Любая программа на SWT начинается с создания объекта Display. Он служит своеобразным контекстом приложения, который содержит необходимые методы для обращения к ресурсам системы и обеспечивает цикл событий. Следующим шагом будет создание не менее важного объекта Shell. Shell представляет собой обычное окно операционной системы. В конструктор shell передается Display, чтобы создать окно верхнего уровня.

    Так как мы создаем виджет, нам не нужно отображать стандартное обрамление окна и кнопки управления, для этого мы указали флаг NO_TRIM. Для фона мы будем использовать картинку — прямоугольник с закругленными углами. В принципе, окно SWT может принимать любые формы. Чтобы добиться такого эффекта, используем класс Region. Все, что нужно, — добавить в этот класс все видимые точки из картинки фона, пропуская прозрачные.

    В изображениях разных форматов прозрачность задается по-разному, поэтому и извлекается информация о прозрачных областях тоже не одинаково. Создаем область фона и добавляем туда все видимые точки:

    Устанавливаем форму окна:

    Теперь нужно создать слушателя событий для окна. Нас будут интересовать события рисования окна, события мыши и нажатия клавиш, чтобы окно можно было передвигать по экрану.

    Итак, по нажатию на клавишу Esc окно закроется. При нажатии левой клавиши мыши на области окна запомним координаты нажатия. При движении мыши с зажатой левой клавишей — передвигаем окно на экране соответственно движению. При событии перерисовки — рисуем картинку фона, используя графический контекст GC.

    Назначим слушатель соответствующим событиям окна:

    Устанавливаем размер окна равным размеру изображения:

    Открываем окно и запускаем цикл событий:

    Не забываем в конце освободить использованные ресурсы:

    Запустив программу на этом этапе, мы получим прямоугольничек, который можно двигать мышкой и закрывать по Esc.

    Настало время добавить содержания. Будем отображать текущую погоду в виде иконки состояния (солнечно, дождь, снег…), показаний температуры и времени последнего обновления.

    Для расположения графических компонентов в окне в нужном виде используются менеджеры компоновки. Менеджер компоновки занимается не только расположением компонентов, но и изменением их размеров при изменении размеров окна. Для нашего виджета будем использовать GridLayout. Этот менеджер располагает компоненты в ячейках воображаемой таблицы. Создаем GridBagLayout на две колонки с различной шириной колонок (флаг false в конструкторе), устанавливаем его в качестве менеджера компоновки окна:

    Для картинки статуса используем компонент Label. В качестве родителя передаем объект окна. Вторым параметром можно установить стиль компонента. Для каждого компонента набор возможных флагов стиля разный, их можно посмотреть в документации или прямо в исходниках компонента.

    Флаги в классе GridData означают, что метка будет располагаться слева вверху, будет растягиваться горизонтально и вертикально (флаги, установленные в true) при наличии свободного места и занимает одну строку и один столбец таблицы компоновки.

    В SWT нет прозрачного фона компонентов, и позади картинки статуса будет красоваться белый фон, чего, конечно, не хотелось бы. Поэтому создадим объект Color с цветом фона окна:

    В конце программы этот объект также необходимо очистить, вызвав метод dispose. Устанавливаем цвет фона и картинку статуса, которую можно загрузить из файла точно так же, как мы загрузили картинку фона вначале:


    Теперь добавим Label с текущей температурой и расположим его в правой верхней части окна:

    Установим какую-нибудь температуру:

    Для записи температуры по Цельсию используется юникодный номер соответствующего символа со служебными символами \u.

    Шрифт по умолчанию для текстовых меток слишком маленький. Так что создадим новый, побольше:

    FontData[] fD = temperatureLabel.getFont().getFontData(); fD[0].setHeight(30); fD[0].setStyle(SWT.BOLD); Font newFont = new Font(display, fD[0]); temperatureLabel.setFont(newFont); Шрифт, как и другие ресурсные объекты, нужно освобождать. Для этого воспользуемся слушателем события разрушения метки:

    Наконец, добавим метку с описанием погодных условий:

    Текст может быть довольно длинным, так что при создании метки указываем флаг WRAP, чтобы текст автоматически разбивался на несколько строк при нехватке места. Расположим компонент по центру и разрешим ему заполнить все горизонтальное пространство. Также укажем, что компонент занимает два столбца таблицы компоновки. Запускаем и получаем окошко с картинки «Виджет погоды».

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

    Swing: всегда свежие новости

    На Swing мы напишем виджет для отображения RSS-новостей. Начинаем, как и в прошлый раз, с создания окна. Класс, реализующий функционал стандартного окна в Swing, называется JFrame. По умолчанию закрытие окна приложения в Swing не приводит к остановке программы, так что лучше прописать, как должно себя вести окно при закрытии:

    Для представления новостей лучше всего подходит таблица. Swing построен на паттерне «Модель —представление — контроллер» (MVC). В архитектуре MVC модель предоставляет данные, представление отвечает за отображение данных (например, текст, поля ввода), а контроллер обеспечивает взаимодействие между моделью и представлением. Таблица хорошо демонстрирует этот подход. Для представления данных используется класс, реализующий интерфейс TableModel.

    Для хранения информации о доступных новостях заведем класс FeedMessage c полями для названия статьи и даты выхода:

    Чтобы упростить и ускорить разработку, наследуем нашу модель данных от класса AbstractTableModel, который предлагает готовую реализацию почти всех методов интерфейса TableModel.

    Метод fireTableDataChanged сообщает представлению, что модель данных изменилась и необходима перерисовка.

    Создаем таблицу и немного изменяем ее вид, чтобы она была больше похожа на виджет. Убираем линии между строками и столбцами, увеличиваем высоту строки и убираем заголовок таблицы с названиями колонок:

    Теперь займемся внешним видом ячеек. Swing позволяет назначать отдельные классы представления для разных типов данных. За отрисовку отдельных ячеек таблицы отвечает класс, наследующий интерфейс TableCellRenderer. По умолчанию используется DefaultTableCellRenderer, который представляет собой текстовую метку.

    Назначим свой отрисовщик ячейки для данных типа String. Изменим стандартный цвет шрифта и сделаем чередующийся цвет фона, чтобы улучшить читаемость.

    Чтобы таблица начала использовать наш отрисовщик, необходимо добавить метод, который возвращает тип данных для каждой ячейки, в модель данных:

    Новостей может быть много, поэтому поместим таблицу на панель прокрутки и сделаем ползунок прокрутки невидимым, чтобы он не портил нам дизайн виджета:

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

    Как и в прошлый раз, уберем стандартное обрамление окна. А в качестве заголовка окна будем использовать стилизованную текстовую метку, которую разместим вверху окна.

    В отличие от SWT, объекты «цвет» и «шрифт» освобождаются автоматически, так что можно больше не переживать за утечки памяти.

    Добавляем слушатели мыши, чтобы окно можно было двигать по экрану.

    Теперь поменяем форму окна на прямоугольник с закругленными углами. Лучше всего это делать в слушателе компонента, так как, если размер окна изменится, форма окна будет правильно пересчитана:

    Устанавливаем размер окна, убираем обрамление и делаем окно полупрозрачным.

    Наконец, открываем окно в графическом потоке. SwingUtilities.invokeLater(new Runnable() < public void run() < frame.setVisible(true); >>);

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

    JavaFX: послушаем музычку

    И наконец, гвоздь сезона — JavaFX. Воспользуемся его мультимедийными возможностями и компонентом для построения графиков и сделаем простенький эквалайзер.

    Для начала наследуем класс виджета от Application. Это основной класс приложения в JavaFX. Application содержит основные методы жизненного цикла приложения. Компоненты формы создаются в методе start, аргументом которому служит класс Stage. Stage представляет собой окно программы. Изменим стиль окна на TRANSPARENT, чтобы убрать обрамление и кнопки. В Stage помещается класс Scene, в котором задаются размеры окна и цвет фона. В Scene, в свою очередь, передаем класс Group, в который будем помещать дочерние компоненты:

    Для отображения эквалайзера используем столбиковую диаграмму, по осям которой будем отображать частоту и мощность звука:

    Заполняем диаграмму начальными данными:

    Создаем прямоугольник с закругленными углами, чтобы придать виджету соответствующую форму:

    Добавляем оба компонента к группе:

    Назначаем слушателей мыши к группе, чтобы двигать окно по экрану:

    Загружаем песню в плеер:

    Добавляем слушатель, который будет обновлять столбиковую диаграмму:

    Делаем сцену видимой и запускаем песню:

    И наслаждаемся такой вот красотой.

    Заключение

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

    3 библиотеки для создания графического интерфейса пользователя (GUI)

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

    В Python начать GUI программирование просто, но первый шаг – это выбор пакета для создания GUI. К счастью, у программистов, которые хотят создать простой способ взаимодействия пользователей с их программами, есть много вариантов.

    Но прежде всего задумайтесь: действительно ли нужно, чтобы ваше приложение имело традиционный графический интерфейс пользователя вообще? Для некоторых программ ответ очевиден. Если ваше приложение по своей природе графическое или будет запускаться локально на одной машине, то да, вам, вероятно, следует создать десктопный интерфейс. Часто это становится очевидным исходя из того, что вы проектируете.

    Но для более общих программ, не сбрасывайте со счетов командную строку или веб-интерфейс.
    Командная строка обладает многими преимуществами: скоростью, удаленным доступом, возможностью повторного использования, возможностью записи сценариев и контролем, что может быть более важным для пользователей вашего приложения, чем графический интерфейс, и существует множество библиотек, таких как, например, Click, Cement и Cliff, которые помогают создать великолепные программы командной строки.

    То же самое с веб-интерфейсом. Даже для программы, предназначенной для локального запуска, это может быть решением – особенно, если есть вероятность, что пользователи могут разместить ваше приложение удаленно, а такие проекты, как Django, Flask или Pyramid, делают это просто.
    Можно даже использовать библиотеку наподобие pywebview, чтобы обернуть веб-приложение в более естественное окно GUI.

    Или еще можно использовать фреймворк Pyforms для создания единообразного представления в Интернете, командной строке и на рабочем столе, и это все на базе одного кода.

    Все еще уверены, что хотите построить графический интерфейс? Отлично, вот три фантастических библиотеки с открытым исходным кодом.

    PyQt реализует популярную библиотеку Qt. Вы, возможно, уже знакомы с Qt – вы могли работать с Qt на другом языке. PyQt позволяет разрабатывать приложения на Python, и в то же время пользоваться инструментами и знаниями большого сообщества Qt.

    PyQt имеет два вида лицензии – коммерческую и лицензию GPL, в отличие от самого проекта Qt.

    Tkinter

    Если бы был один пакет, который можно было бы назвать «стандартным» инструментарием GUI для Python, это был бы Tkinter. Tkinter – это оболочка вокруг Tcl / Tk, впервые появившегося в начале 90-х годов. Преимущество Tkinter – огромное количество ресурсов, включая книги и примеры кода, а также большое сообщество пользователей, которые могут помочь, если у вас есть вопросы. Примеры просты и понятны для новичка.

    Tkinter доступен под лицензией Python, поверх лицензии BSD Tcl / Tk.

    WxPython

    WxPython переносит кросс-платформенную графическую библиотеку wxWidgets с родного языка C ++ на Python. WxPython – это немного более современный подход, который выглядит более натуральным, в разных операционных системах, чем, например, Tkinter, потому что он не пытается создать свой собственный набор виджетов (хотя они могут быть тематически похожими на его исходные компоненты). Он тоже прост в освоении, и сообщество разработчиков на WxPython посоянно растет.

    WxPython использует wxWindows Library License своего родительского проекта, который одобрен OSI.

    Это, конечно, не все доступные варианты. Десятки других опций есть на странице «Программирование GUI на Python» на официальной вики-странице Python Software Foundation.

    А если захочется что-нибудь сделать что-нибудь не похожее на традиционное рабочее окно – например видео игру, то для этого в Python тоже есть хорошие библиотеки – например, pygame и pyglet.

    GUI с нуля за час. Ищем решения типовых задач при создании графического интерфейса

    Lead iOS-разработчик в neoviso Игорь Шавловский в материале «Давайте напишем Windows!» предложил «зажигать пиксели один за другим». Теперь переходим к рендеру и разбираем код из репозитория на GitHub. Для её запуска используйте класс CustomApplication.

    Давайте подумаем, как именно будет организована работа приложения. Для этого зададим правильные вопросы: кто, когда, где и что должен рисовать?

    Так как корневой элемент интерфейс — это рабочий стол, рисовать следует именно его, при этом команды должны уходить на драйвер устройства (VideoDevice). Допустим, устройство есть, рабочий стол тоже. Но как их связать вместе? Нужен посредник (Application), который будет готовить девайс и через графический контекст передавать на него команды рендера с рабочего стола. Когда он должен это делать

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

    Как работает GUI-приложение

    Что ещё может происходить в UI-цикле? Если нам надо переместить окно на рабочем столе или сменить текст в лейбле, добавить дочерний элемент, можем ли мы сделать это во время отрисовки? Очевидно, эти операции могут быть небезопасными. Например, можно добавить элемент в массив childs в тот момент, когда он находится в процессе перечисления, что вызовет ошибку. И это не единственное тонкое место — изменение размеров элементов, текста, цвета и других параметров во время отрисовки могут создать проблемы в коде рендера.

    Правильным решением здесь будет синхронизация процесса рендера и модификации параметров элементов интерфейса. Поскольку подобные операции (назовём их invoke) в GUI происходят довольно часто, хорошим решением будет просто выполнять их в том же потоке, что и рендер, непосредственно перед ним. Для этого сделаем очередь операций и выберем метод добавления операции в очередь (invokeLater).

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

    За пределами приложения лежат устройства ввода-вывода, которые симулированы классами из пакета by.dev.gui.driver, а «виртуальной машиной» является простой JFrame, который получает от «видеоустройства» картинку (Image) и посылает события мыши в соответствующее «устройство» ввода.

    Монитор представляет собой буфер пикселей, в который и рисуется интерфейс, а в конце каждого UI-цикла созданная им видеостраница посылается на «матрицу».

    Для управления буфером пикселей создан класс Texture. Ещё вы могли заметить, что GraphicsContext потерял свойство offset и теперь всегда рисует в текстуру, начиная с верхнего левого угла (это изменение будет объяснено ниже при рассмотрении алгоритмов полупрозрачности), у него появился метод fill() для монохромной заливки прямоугольной области, а также наследник SafeGraphicsContext. Дело в том, что дочерние элементы зачастую могут выходить за границы своих родителей, следовательно, их пиксели могут отрисоваться в несуществующие области текстуры. Чтобы не производить проверку попадания в границы родителя каждый раз, проще всего делать её на стороне графического контекста.


    Барабанная дробь! Вот мы и создали все инструменты, чтобы всё вышесказанное обрело хоть какой-то практический смысл.

    Пишем собственные элементы интерфейса и рисуем их

    Начнём с рабочего стола (Desktop). Это будет просто область, залитая одним цветом, с инструментом для управления окнами приложений. Фактически всё, что нужно для нашего примера, — это механизм выбора и контроля текущего активного окна, а также доставки событий мыши до элементов интерфейса, над которыми они произошли.

    Окно приложения (Window) — тоже залитый прямоугольник с границей и заголовком, на котором лежит кнопка закрытия окна. Кнопка (Button) — это прямоугольная область, которая меняет цвет при нажатии на неё и посылает событие (callback) по окончанию нажатия.

    И мышь! Возьмите её в руки, повертите, определите, что вы можете с ней сделать: мышь можно перемещать и нажимать на ней кнопки. Отлично, значит это и будет интерфейсом нашего драйвера. Поскольку мышь — устройство ввода, то для него мы не отправляем управляющие команды и лишь принимаем их через MouseDeviceCallback. Отметим, что для драйвера мыши могут существовать и команды управления: установка позиции курсора, чувствительности и т. д.

    Осталось доставить входящие в приложение события мыши (MouseEvent) до их конечных получателей — элементов интерфейса. Первым делом надо решить, кто будет управлять этой доставкой — это должен быть класс, который имеет доступ ко всей иерархии элементов, а в нашем случае это как раз рабочий стол, приложение будет сообщать ему о новом событии мыши, вызывая метод onMouseEvent, остальное будет на его совести. Что же именно он должен делать?

    Сначала надо придумать алгоритм поиска элемента, который находится в точке, в которой произошло событие мыши — nodeForMouseEvent(). Для этого надо посмотреть на рабочий стол, навести мышь на какую-нибудь кнопку и подумать, как найти именно её, зная только список элементов, лежащих на рабочем столе. И также держать в голове, что в случае, если поверх вашей кнопки лежит окно, оно перехватит событие, и кнопка его не получит. Значит событие получает тот элемент, который лежит выше всех в этой точке. Учитывая порядок отрисовки дочерних элементов в методе draw(), можно понять, что чем выше индекс элемента в массиве childs, тем позже он рисуется. В том числе если положить кнопку А на кнопку В, то кнопка А является получателем события мыши только в том случае, если нажатие не попадает в область кнопки В и попадает в область кнопки А. Так как нарисованный последним элемент лежит выше всех, искать получателя события надо рекурсивно, начиная с родительского элемента в порядке убывания индекса элемента.

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

    Как можно реализовать drag-n-drop для окон?

    Речь идёт о механизме перетаскивания окна зажатием мыши на его заголовке. Допустим, событие нажатия мыши происходит над заголовком конкретного окна. Окно запоминает, что его в данный момент «тянут» и ждёт событий перемещения мыши и отжатия кнопки. Однако эти события дойдут до окна только в том случае, если пользователь перемещает мышь исключительно в пределах элемента, отвечающего на это событие, то есть заголовка окна. Если же мышь выйдет за его пределы, то nodeForMouseEvent уже не найдёт его как получатель события, и перемещение окна прекратится. Более того, если окно не получит события отпускания кнопки мыши, оно так и останется навсегда в режиме перетаскивания.

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

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

    Можно придумать несколько таких механизмов, но для реализации нашей задачи (событие кнопки при отпускании мыши и перетаскивании окон) можно обойтись малой кровью — подходом с запоминанием активного элемента. Это элемент, который в данный момент эксклюзивно отвечает на события мыши. Пока он активен, рабочий стол будет направлять события исключительно ему, независимо от того, находится ли мышь над элементом или элемент в границах своего родителя. Пока активного элемента нет, рабочий стол будет предлагать событие элементу под курсором, вызывая метод doMouseEvent(). Метод должен возвращать boolean, который говорит, готов ли элемент стать активным. Как только один из элементов вернёт true, он станет активным и будет перехватывать все события мыши. Продолжаться это будет до тер пор, пока doMouseEvent() не вернёт false, что означает — элемент более не активен, и все другие опять могут получать события мыши. Этого простого и немного костыльного подхода хватит, чтобы удовлетворить наши потребности.

    Создайте приложение сами!

    Имея эти элементы, события мыши и класс Application, мы можем создать простое приложение. Предлагаю написать его с точки зрения разработчика, то есть используя написанный нами GUI как фреймворк. Код приложения вы можете найти в классе CustomApplication.

    Усложняем задачу и добавляем полупрозрачность

    Напоследок предлагаю усложнить задачу и добавить полупрозрачность. Это типовое задание, которое встречается при разработке интерфейса. Однако без аппаратного ускорения подобная задача становится сравнительно сложной. Давайте разбираться вместе.

    Всегда помните — начинаем с малого, с частного, вычленяем простые операции, задаём нужные вопросы. И главный вопрос в каждой задаче — а в чём же она состоит, что такое полупрозрачность? И опять всё сводится к одному пикселю: оригинальный пиксель, лежащий «снизу», переписывается новым пикселем, но при этом не полностью, а в какой-то степени, называемой alpha. Alpha обычно измеряется в тех же единицах, что и каналы цвета, то есть в нашем случае — числом от 0 до 255.

    Теперь надо «поиграть» с alpha и понять, какая математика за этим стоит. Техническим языком эта математика называется blending, то есть способ наложения двух цветов. Можно открыть Photoshop и посмотреть как результирующий цвет меняется в зависимости от показателя прозрачности и способа смешивания.

    Но нам нужен универсальный и простой способ смешивания. Проще всего понять его в мысленном эксперименте. Представляем себе чёрный квадрат и кладём поверх белый, только полностью прозрачный. Чёрный квадрат останется чёрным, потому что белый не сможет на него повлиять. Теперь представляем, что alpha белого квадрата максимальна, то есть теперь он полностью перепишет чёрный цвет своим. А сейчас мысленно погоняйте в голове прозрачность от минимуму к максимуму и наоборот. Промежуточные цвета будут серыми, то есть все каналы равномерно будут перетекать от 0 к 255, выдавая в результате градиент серого, и чем больше прозрачность белого, тем более тёмный серый мы получаем. Теперь надо увидеть правило, согласно которому всё это работает. Фактически мы имеем простое параметрическое уравнение:

    color = source + (dest — source) * (alpha / alphaMax);

    То есть для примера с белым и чёрным цветами по каждому каналу уравнение будет выглядеть так:

    red = green = blue = 0 + (255 — 0) * (alpha / 255);

    Всегда проверяйте свои формулы, подставляя значения из известных предельных случаев!

    0 + (255 — 0) * (0 / 255) = 0 (при полной прозрачности чёрный остаётся нулём);
    0 + (255 — 0) * (255 / 255) = 255 (при нулевой прозрачности цвет становится белым белый).

    Итак, мы поняли, как смешать два пикселя с заданным коэффициентом. Следующий этап — понять, как это смешивание встроить в наш рендер. Во-первых, добавим к Node свойство alpha. Во-вторых, подумаем над самым простым случаем, когда над рабочим столом рисуется одна полупрозрачная кнопка. Сама кнопка может менять цвет в зависимости от своего состояния, на ней может находиться текст, картинка, то есть внешний вид кнопки определяется только ей самой, он не зависит от родителя. Значит альфа должна быть применена в момент рисования уже «готовой» кнопки на рабочий стол.

    Сработает ли наш старый подход, при котором сначала отрисуется рабочий стол, а поверх — кнопка? В момент рисования пикселя кнопки она возьмёт текущее значение цвета этого пикселя и прибавит к нему собственный цвет с альфой по описанной сверху формуле и запишет результат поверх исходного цвета. Всё выглядит нормально.

    Но что если эта полупрозрачная кнопка будет лежать на полупрозрачном окне? Тогда сначала опять же отрисуется рабочий стол, потом поверх его окно. И вроде всё хорошо? Но рисование кнопки всё испортит. Дело в том, что кнопка относительно рабочего стола полупрозрачна не только на свою полупрозрачность, но и на полупрозрачность окна, которая складывается мультипликативно (так как альфа окна «наследуется» всеми его элементами). Даже если мы сложим альфу кнопки с альфой окна и используем её для отрисовки элемента, мы не получим правильный результат. Результатом отрисовки дочернего элемента на родителя с полупрозрачностью будет исключительно смесь цветов самого элемента и родителя, независимо от более верхних уровней иерархии. Их сумма в свою очередь уже будет рисоваться на следующий слой таким же образом.

    Это отлично видно, если мы представим, что альфа окна равна 50%, а кнопка не прозрачна вообще. В этом случае теоретически все пиксели окна, покрытые кнопкой, в результате должны быть лишь смесью половины рабочего стола и половины кнопки, но в момент рисования кнопки эти пиксели уже «загрязнены» окном. Математически это выглядит так:

    C = Cdt*(1 — Aw) + Cw * Aw;

    Cw = Cw * (1 — Ab) + Cb * Ab;

    C = Cdt*(1 — Aw) + (Cw * (1 — Ab) + Cb * Ab)* Aw;

    Если Ab = 1 и Aw = 0.5, то

    C = 0.5 * Cdt + 0.5 * Cb;

    C — цвет результата, Cdt, Cw, Cb, Aw, Ab — цвета и альфа элементов (dt — desktop, w — window, b — button).

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

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

    Осталось запустить приложение!

    И убедиться, что всё работает корректно. Конечно, можно заметить, что всё работает очень медленно, но это не удивительно: мы рисуем пиксель за пикселем, не используя ни возможности быстрого копирования в памяти, ни аппаратное ускорение. Скорость алгоритмов не была целью. Главное — найти чистые алгоритмы путём анализа, и мы с этим справились.

    Напоследок сделам минимальную оптимизацию: не надо перерисовывать элементы при каждом цикле рендера, особенно, если учитывать, что в данный момент все они — лишь залитые прямоугольники. Для этого у элемента вводим свойство needsRedraw, которое и будет индикатором того, что его текстуру следует перерисовать. Это свойство должно выставляться в true при каждом изменении свойств, влияющих на внешний вид элемента. Более того, это должно происходить не только с самим элементом, но и со всеми его родителями, так как их текстуры зависят от внешнего вида всех дочерних элементов.

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

    Python-сообщество

    Уведомления

    #1 Дек. 12, 2008 10:03:43

    На чем сделать быстрый GUI?

    Подскажите на чем лучше сделать GUI, чтобы работало быстро и не подтупливало. Причем контролов будет много, если в один момент времени может показываться до сотни, различных элементов, редактируемые кривые, средства управления координатными осями, кнопки различных типов, маленькие полупрозрачные окна с полями, поверх основного окна. Также видимо будет самописные контролы.
    Еще, видимо, немаловажно — такая вещь как удобство написания своих контролов.

    Наиболее близко к тому что я представляю и из того что я видел это интерфейс Blender (хотя задачи совсем не близкие :) ).

    Пока что есть три варианта:
    1. GUI на С++/wxWidgets — почему то не хочется….
    2. GUI на Python/wxPython — вероятно будет подтупливать.
    3. GUI на Python/PyQT — опыта вообще нет.

    У кого-нибудь есть опыт построения громоздких интерфейсов на Python? У кого-нибудь это тормозило? Может лучше все-таки на С++, а Python оставить чисто для скриптов? Поделитесь мнением.

    P. S. Чтобы не было вопросов, программа для управления одним научным прибором.

    #2 Дек. 12, 2008 10:18:47

    На чем сделать быстрый GUI?

    Не думаю что язык в данном случае будет слабым звеном, скорее это сам GUI или, скажем, X’ы. Если вам покажется стандартный набор виджетов недостаточным и начнете писать что-то своё, в этом случае, роль языка (нативный, интерпретируемый) будет более значимой. Но и опять же. Все необходимые виджеты (и др. ресурсоемкий код) можно написать на “быстром языке”, а логику на Python.

    Я не имею опыта создания “больших” интерфейсов. Так что кто будет пошустрее, Qt, GTK или WinAPI, не скажу. Вроде, пока, винда остается самой быстрой в этом плане.

    p.s. Ну в Blender всё ручками было сделано :-).

    Отредактировано (Дек. 12, 2008 10:19:17)

    #3 Дек. 12, 2008 10:27:31

    На чем сделать быстрый GUI?

    Да, забыл еще кроссплатформенность тоже обязательна :). Как мне кажется, это не очень правильное требование, но как сказали, так и надо делать.
    bw, спасибо. Если я правильно понял, то аналогичные GUI на wxWidgets и wxPython — работают практически с одинаковой скоростью?

    Отредактировано (Дек. 12, 2008 10:34:43)

    #4 Дек. 12, 2008 10:36:26

    На чем сделать быстрый GUI?

    У меня на работе есть проект, ГУИ писал на wxPython. не могу сказать, что контролов очень много, но достаточно для того, чтобы приложение подтормаживало…притом от машины мало зависит…сравнивал некоторые виджеты с аналогичными в Qt и она показала себя чуть быстрее. В данный момент весь ГУИ переписывается на PyQt :)

    #5 Дек. 12, 2008 14:48:20

    На чем сделать быстрый GUI?

    По опису прогнозуємої програми дійсно дуже схоже на інтерфейс Blender -а … гляньте на чому він зроблений — вихідні коди відкриті…
    може можливо використати їх бібліотеки(якщо там щось своє) ?
    Іще як варіант GTK+ Cairo +OpenGL
    або ж SDL
    Часом рендерингу в режимі реального часу непотрібно? Бо якщо так — то тоді щось легке типу Motif/FLTK +OpenGL

    #6 Дек. 12, 2008 15:07:32

    На чем сделать быстрый GUI?

    http://www.blender.org/development/architecture/
    Там таки можна використати їх UI framework (іде поверх SDL/OpenGL)

    #7 Дек. 12, 2008 15:38:06

    На чем сделать быстрый GUI?

    clopomor, спасибо, рендерить в реальном времени, не надо, только работа с изображениями, которою с лихвой покрывает PIL(Python) или cxImage (С++). SDL-выглядит немного мощновато, мне же в принципе, нужно GUI сделать, который бы как можно меньше подтупливал.

    #8 Дек. 12, 2008 16:15:44

    На чем сделать быстрый GUI?

    ну тоді вам підійде будь-яка бібліотека — дивіться ту яка надає максимум потрібних вам компонентів…
    підтуплювати інтерфейс буде лише коли його перемальовування буде очікувати завершення якоїсь дії(чи внутрі елементів форми щось постійно рендеритиметься), тому просто виділіть сам процес обробки в окремий процес/тред і керуйте ним … а в формі лише відорбражайте результат

    Цукерберг рекомендует:  15 действительно креативных страниц ошибки 404
    Понравилась статья? Поделиться с друзьями:
    Все языки программирования для начинающих