0

Графическое приложение на java

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

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

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

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

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

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

Недостатки:

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

заключение:

В настоящее время AWT используется крайне редко — в основном в старых проектах и апплетах. Oracle припрятал обучалки и всячески поощряет переход на 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.

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

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

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

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

Недостатки:

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

Заключение:

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

Как выглядит 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 мы напишем виджет для отображения 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. Воспользуемся его мультимедийными возможностями и компонентом для построения графиков и сделаем простенький эквалайзер.

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

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

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

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

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

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

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

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

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

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

Простой эквалайзер

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

Занятие 6. Графические интерфейсы: первое окно

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

public class MoneyForNothing extends JFrame <

setTitle ("Добро пожаловать в Money for Nothing");

setSize (new Dimension (600, 400));

public static void main (String [] args) <

MoneyForNothing mfn = new MoneyForNothing ();

А вот этот же код в окне редактирования FAR-а:

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

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

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

Сначала решим вторую задачу – центровка окна. Тут мы рекомендуем остановиться и подумать – как бы вы это сделали?

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

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

Dimension sSize = Toolkit.getDefaultToolkit ().getScreenSize (),

if (fSize.height > sSize.height)

if (fSize.width > sSize.w >

setLocation ((sSize.width – fSize.width)/2,

непосредственно за строкой setSize (new Dimension (600, 400)); в конструкторе. Внесите необходимые изменения в исходный код, откомпилируйте программу и запустите на исполнение; окно должно появиться в центре экрана монитора.

Теперь несколько слов о внешнем виде окна. Его странный вид объясняется тем, что разработчики Java стремились добиться того, чтобы вне зависимости от аппаратной платформы и программной «начинки», все графические элементы (окна, кнопки, списки и проч.) имели единую отрисовку и единую цветовую гамму. Для этого они разработали специальный стиль, который назвали «METAL». Если разработчик не предпримет специальных усилий, то элементы графического интерфейса в его программах будут выглядеть именно в этом стиле, без учета особенностей конкретных компьютеров и их программного обеспечения. В отдельных случаях в этом есть смысл, но все-таки, согласитесь, что гораздо лучше, если программа, запущенная на Windows будет похожа на windows-программу, а запущенная на LINUX будет похожа на linux-программу. Добиться этого легко. Все, что нужно – включить в точку входа, перед созданием экземпляра класса следующий код:

Читайте также:  Как в windows 10 сделать основной монитор

catch (Exception lfe) <>

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

В зависимости от настройки свойств экрана вашего монитора отображение окна будет отличаться; мы используем классическую тему Windows XP. У вас это же окно может выглядеть, например, так:

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

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

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

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

(подсказка: ищите информацию по ключевым словам javax и JFrame). Так что, засучите рукава и удачи!

Может случиться, что сформированное окно будет полностью или частично невидимо (из-за того, что вы неправильно рассчитали координаты его вывода на экран). Кнопки управления окна могут также оказаться недоступными. Как же прервать работу приложения не снимая задачу в «Диспетчере задач» или не перезагружая компьютер?

Поскольку мы запускаем программы на исполнение из FAR-а, то прерывание исполнения программы на Java достигается нажатием комбинации клавиш Control-C (здесь «C» – латинская буква, не путайте ее со сходной по начертанию буквой кириллической).

Все уроки . 10.02.2014

Основы программирования GUI на языке Java. Урок 1.

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

И так, какие инструменты нам необходимы:

  • Java Virtual Machine (OpenJDK или Oracle JDK)
  • Intellij IDEA (или другое IDE для Java)

После установки необходимого софта, открываем Intellij IDEA и создаем новый проект: File -> New Project…

Я назвал проект guiBase. Как видно на скрине, папка src не содержит ничего, поэтому создаем в ней наш главный класс, содержащий функцию main.

Содеражние главного класса видите выше. Мы уже сейчас можем создать проект (Build project) и запустить его (Run). Внизу в терминале вашего IDE вы увидите сообщение “Hello, Govzalla!“. Но как вы сами поняли — GUI он не поддерживает.

На данном этапе у нас уже есть работающая программа, но без поддержки GUI. А сейчас в той же папке src создадим GUI Form: New -> GUI Form

Открываем созданную GUI форму, нажимаем на JPanel и задаем его идентификатор в поле field name, я задал panel.

После чего перетаскиваем на форму с правой стороны JTextField, JPasswordField и JButton:

Осталось добавить код и связать нашу форму с ним. Когда мы добавляли форму MainWindow, автоматически создался и класс MainWindow, этот класс является классом созданной формы, т.е. именно этот класс будет обслуживать все события данной формы.

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

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

В данный момент мы имеем форму MainWindow и класс MainWindow расширенный с помощью JFrame. Сейчас нам необходимо определить все добавленные GUI элементы как содержание класса MainWindow
this.getContentPane().add(panel);
После чего содержание файла MainWindow.java будет изменено следующим образом:

Если попробуете запустить код, вы снова увидите то же самое сообщение “Hello, Govzalla!“. Дело в том, что мы создали класс и форму к нему, но не создали инстанцию этого класса.

Пришло время изменить файл Main.java и добавить туда код создания нашего GUI:

Нажав на кнопку Button вы заметите, что программа никак не реагирует. Дело в том, что мы еще не добавили слушатель (Listener) для событий (Events) кнопки Button.

Слушатель событий (Event listener) JButton должен быть имплентацией адаптера ActionListener, поэтому добавим следующий код в тело класса MainWindow:

Метод actionPerformed() будет обрабатывать все события кнопки button1, но для начала еще необходимо указать кнопке button1 какой класс будет обрабатывать, поэтому добавим следующий код в конструктор класса MainWIndow:
this.button1.addActionListener(new MyButtonListener());
Чтобы наш обработчик не был бессмысленным добавим следующий код в метод actionPerformed():

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

admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *