Программирование на Python и Objective-C в Mac OS

Программирование на Python и Objective-C под Mac OS и для iPhone / iPod Touch

Widget logic команды: Widget Logic — плагин для сайдбаров

Содержание

Widget Logic — плагин для сайдбаров

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

Когда возникают подобного рода ситуации, связанные с распределение контента в сайд-баре по страницам, вам пригодится такой простой и полезный Widget Logic.

Освоив этот плагин, вы сможете не только расположить виджеты исключительно на тех страницах, где они нужны, но и избежать такой неприятности, как неопрятный и захламленный сайдбар. Благодаря Widget Logic вы обретете полный контроль над WP-виджетами.

Установка плагина Widget Logic абсолютно стандартна и я не буду заострять на этом процессе ваше внимание, а лучше расскажу о том, что произойдет после нее.

Итак, после активации плагина под каждым виджетом появляется строка Widget Logic, с помощью которой его можно прикрепить к определенной странице или группе страниц. Наверняка, на данном этапе у многих неопытных пользователей, не очень хорошо знакомых с WordPress может возникнуть резонный вопрос: «А что делать дальше?»

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

Условные теги (Conditional tags) – это своего рода команды, с помощью которых WP-шаблону можно диктовать свои условия отображения того или иного контента.

Вот, несколько тегов, без которых работа с Widget Logic невозможна:

is_home () — отображать виджет только на главной странице блога

!is_page (‘about’) — отображать виджет на всех страницах блога, за исключением страницы about;

is_single () — показывать только на страницах единичных постов;

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

is_page () — отображать виджет исключительно на страницах блога;

is_search () — отображать только на странице с результатами поиска по блогу;

is_archive () — отображать только на страницах с архивами блога;

!is_home () — отображать на всех страницах блога кроме главной;

is_admin () — виджет виден только администратору блога.

Полный список таких “условных тегов” можно найти в кодексе WP на сайте wordpress.org

Теоретически то, что делает плагин, можно сделать и вручную, встроив условные теги в шаблон, но это лишняя головная боль. С плагином все гораздо проще, хотя знать, что такое «условные теги» и с чем их едят все же нужно, хотя бы для того, чтоб использовать Widget Logic. Вообще, условные тэги – сами по себе очень мощный инструмент, с помощью которого можно реализовать многие задумки, касаемые облагораживания WP-блога, впрочем, это тема уже совсем для другой статьи.

Официальная страница плагина

Apple Dashboard — это.

.. Что такое Apple Dashboard?

Dashboard — программное обеспечение Apple Inc, содержащее небольшие программы, называемые «виджетами».

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

Dashboard является частью Apple Mac OS X 10.4, 10.5 и 10.6.

Dashboard Mac OS X 10.4 по умолчанию, в стандартной комплектации содержит 14 виджетов:

  1. Адресная книга (Address Book)
  2. Калькулятор (Calculator)
  3. Календарь (Calendar)
  4. Словарь (Dictionary)
  5. Авиасправка (Flight Tracker)
  6. iTunes контроллер (iTunes Controller)
  7. Телефонная книга (Phone Book)
  8. Заметки (Stickies)
  9. Акции (Stocks)
  10. Игра «Пятнашки» (Tile Game)
  11. Переводчик (Translation)
  12. Преобразователь единиц измерения (конвертер) (Unit Converter)
  13. Погода (Weather)
  14. Мировое время (World Clock)

Используя систему обновлений Mac OS X, компания Apple Inc. может добавлять новые виджеты, и изменять вид и функционал существующих виджетов.

После Macworld 2006 Стив Джобс представил четыре новых виджета, которые стали доступны в Mac OS X 10.4.4:

а также обновлённые виджеты Address Book (Адресная книга) и Calendar (Календарь). Эти виджеты стали доступны в Mac OS X 10.4.4.

С выходом Mac OS X 10.5 появились два новых виджета: 1) Web Clip и 2) Movies. Виджет Web Clip работает только в связке с браузером Apple Safari.

Также пользователь может сам загружать и устанавливать новые виджеты из интернета.

Виджеты вне Dashboard

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

Нужно ввести команду: localhost:~ me$ defaults write com.apple.dashboard devmode YES. Затем заново войти в учётную запись. Если «держать» мышкой виджет и в это время закрыть Dashboard, после чего отпустить виджет, то он останется на рабочем столе.

Ссылки

Лучшие виджеты Mac в 2020 году для повышения вашей эффективности

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

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

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

Руководство по статьеЧасть 1. Что такое виджет?Часть 2. Лучшие виджеты для Mac в 2020 годуЧасть 3. Вывод

Часть 1. Что такое виджет?

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

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

Часть 2. Лучшие виджеты для Mac в 2020 году

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

1. iMyMac PowerMyMac

Чтобы помочь вам очистить ваш Mac, iMyMac PowerMyMac один из лучших виджетов Mac, который можно скачать с их сайта www.imymac.com, Универсальное чистое приложение, содержащее множество модулей, которыми вы можете воспользоваться, например, удаление системных ненужных файлов, удаление приложений с их файлами и помощь в удалении.

  • Автоматически сканирует ваш Mac и показывает список ненужных файлов, которые вы можете удалить
  • Может помочь освободить место на вашем Mac, найдя большие и старые файлы, iTunes Junk и дубликаты файлов
  • Защитите вашу конфиденциальность, так как вы можете удалить историю просмотров во всех ваших браузерах внутри приложения, удалить кеш, куки и даже расширения
  • Вы можете максимизировать полную функцию до 500 МБ бесплатно

Это дешевле, чем другие Инструменты для очистки Mac которые предлагают те же или меньшие функции, поскольку его всего $ 19. 95 / год, и вы можете иметь бесплатную пробную версию перед покупкой.

Получите бесплатную пробную версию прямо сейчас!

2. амфетамин

Одна из замечательных особенностей амфетамина заключается в том, что вы можете использовать его совершенно бесплатно, он не дает вашему Mac спать, даже когда вы закрываете крышку Mac. Звучит потрясающе, правда? Его очень легко использовать; переключатели доступны для пользователей, чтобы они могли легко и быстро изменять свои предпочтения.

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

Мы рассматриваем это как лучший виджет Mac для вашего Mac.

3. HyperDock

Хотя Ampethamine предоставляется бесплатно, вы можете получить этот HyperDock за минимальную стоимость 9.99 долл. США, он предназначен для повышения вашей производительности. Вы можете выбрать приложение, просто переместив курсор на элемент в доке, где вы можете использовать мышь для быстрого запуска окон.

У него есть окно предварительного просмотра пузыря, которое позволяет просматривать его содержимое, не открывая само программное обеспечение, просто наведя курсор на значок, который сделал его лучшим виджетом для Mac. Вы также можете управлять iTunes на док-станции, чтобы видеть подробности о воспроизводимой песне; вы можете перемещаться, так как у него есть опции для приостановки песни и управления громкостью.

4. Параллельный рабочий стол 15

Это позволяет вам использовать приложения Windows на вашем Mac и, например, Microsoft Office, QuickBooks, Visual Studio, Internet Explorer, а также игры, требующие высокой графики и программ САПР. Вы можете использовать все те приложения Windows, которые не влияют на производительность вашего Mac, например, необходимость перезапуска Mac. Хотя вам понадобится лицензия для Windows 10, поскольку она предназначена для обновлений Windows 10 и macOS Catalina. Вы можете получить это за 79.99 долларов в год.

5. Дуэт

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

6. Logic Pro X

Это один из лучших виджетов Mac с великолепными приложениями для создания музыки, если вы любите микшировать или создавать музыку, в этом приложении есть множество функций, которые вам понравятся. Он обладает таким типом интеллекта, который автоматически определяет темп музыки, которую вы создаете, чтобы убедиться, что ваша музыка как раз вовремя.

Одна из функций — барабанщик, поэтому вам больше не придется устанавливать отдельное приложение для барабанов. Он позволяет вам использовать его бесплатно в течение 90 дней, а цена составляет 199.99 долларов.

7. Wunderlist

Если вы любите вести заметки и напоминания, это будет отличное приложение для вас. Это приложение может помочь вам с вашей производительностью. Его платформа доступна для многих устройств, таких как iOS, Windows или Android. Отличная функция — она ​​позволяет синхронизировать все ваши списки независимо от того, какое устройство вы используете. Он имеет виджет, который вы можете добавить в свой центр уведомлений, и вы можете получить его бесплатно.

8. GIMP

GIMP (программа обработки изображений GNU) — это бесплатное приложение для редактирования фотографий и может быть отличным вариантом помимо Adobe Photoshop. Он имеет расширенные и профессиональные функции, которые вы можете использовать, или вы даже можете создать свое изображение самостоятельно. Он также имеет автоматические усилители фото. Включает также фильтры и взаимозаменяемые кисти, а также есть плагины, которые вы можете загрузить из реестра плагинов.

9. Улисс

Это приложение для письма, которое имеет минималистский интерфейс, поэтому вы можете сосредоточиться на текстах, которые вы пишете, и управлять своими документами. Включает облачную функцию, так что у вас будет доступ к вашим документам в любое время и в любом месте, при условии, что есть сетевое подключение, поскольку вы также можете использовать его на своих устройствах, таких как iPad. Помогает вам также перевести ваши произведения в различные форматы, такие как HTML, PDF или электронная книга, а также позволяет публиковать их на WordPress, Это обойдется вам примерно в 45.00 долларов.

10. Microsoft Office 365

Microsoft Office 365 уже доступен в магазине приложений. Если вы привыкли использовать приложения MS Office, то это хорошее приложение для вас. Разница в том, что вы можете получить доступ к Microsoft Apps онлайн, сначала у вас будут основы, такие как MS Word, PowerPoint и Excel, и даже команды.

У вас также будет облачное хранилище One Drive, где хранятся ваши документы, к которым вы также можете получить доступ в любое время и в любом месте, пока у вас есть сеть. У этого есть другие планы, но для личного это может стоить 69.99 $ / год.

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

11. Поставки

Это приложение идеально подходит для тех, у кого много доставок, так как оно поможет вам оставаться организованным и получать уведомления о поступающих доставках. Его интерфейс очень прост для понимания и использования, вы даже можете использовать его как для MacOS, так и для iOS. Если вы хотите отслеживать свои пакеты, вы можете просто нажать Ctrl + New и ввести номер для отслеживания. Вы можете получить его за 4.99 $ и один из лучших доступных виджетов Mac.

12. Менеджер рецептов паприки

Это поможет вам в организации ваших рецептов, если вы делаете много приготовления или выпечки в цифровом виде. Он прост в использовании, так как имеет простой интерфейс. Хорошая вещь об этом — если вы начали с одного рецепта, будущие рецепты будут копировать формат, который вы создали или можете сделать для вас, так что это легко понять. Вы можете получить к нему доступ на других своих устройствах или, если у вас есть iPhone, это стоит $ 39.99.

13. Фантастическая 2

Вы продолжаете забывать встречи или графики? Это приложение может быть огромной помощью, так как это приложение календаря, которое помогает вам управлять и организовывать ваши задачи и действия. Его функция напоминания очень эффективна, интерфейс также легко понять, чтобы повысить производительность. Кроме того, он также совместим с iCloud, поэтому вы можете легко синхронизировать их оба. Это стоит $ 49.99.

14. Франц

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

15. OneCast

Если все телевизоры в вашем доме используются, и вы хотите использовать свой Xbox One, это приложение позволит вам играть с помощью Mac. Это может передавать вашу игру с вашего Xbox на ваш Mac, и все, что вам нужно сделать, это войти в свою учетную запись Xbox Live. Хотя вам нужно подключить его к модему через Ethernet. Стоит около $ 20.00.

16. Хроника

Это лучший виджет Mac для уведомлений, которые доступны на macOS и iOS, поэтому вы можете легко синхронизировать их, загрузив приложение на оба устройства. Это приложение для уведомлений, предназначенное в основном для ваших счетов, оно помогает вам быть в курсе ваших взносов, будь то счета за аренду, электричество или кредитную карту.

Вы можете получить полный контроль, изменив настройки, когда вы хотите получать уведомления, будь то за 3 дня до установленного срока или за день до этого. Вы можете получить его за $ 9.99 на вашем Mac и $ 2.99 на iOS.

Часть 3. Вывод

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

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

Получите бесплатную пробную версию прямо сейчас!

Vedita — Корпоративный сайт компании «Стронг Билдингс»

Описание

Компания «Стронг Билдингс» строит современные быстровозводимые здания под ключ. Основное преимущество: уникальная строительная система «Стронг»® — типовые решения оптимальной комплектации. Компания также разрабатывает индивидуальные проекты любой сложности в сжатые сроки и предоставляет полный спектр услуг для строительства под ключ: разработку документации в собственном конструкторском бюро, производство металлоконструкций на современном роботизированном оборудовании, доставку и монтаж здания силами партнеров компании (партнера-подрядчика можно найти практически в любом регионе России и СНГ).

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

______________________________________

The Strong Buildings company builds modern prefabricated turnkey buildings: industrial, agricultural, sports facilities, warehouses, car dealerships, garages for large vehicles, shopping centers, offices. The main advantage is the unique construction system “Strong”, which allows you to quickly develop an individual project of any complexity or build a serial building, which has already selected the best solutions of enclosing structures, windows, doors, gates. The company provides a full range of turnkey construction services: develops documentation in the construction department, produces metal structures on modern robotic equipment, supplies and installs buildings with partners (partner network allows you to find a contractor in almost any region of Russia and the CIS).

Project Features: the customer needed a complete redesign of the site, it looked outdated, not informative enough, had low conversion rates. Users had to call to order and clarify the details, and managers had to ask for extra information before reporting the price and technical details of the project — some potential customers were eliminated at the clarification stage.

Поставленная задача и ее решение

Задача — повысить доверие клиентов к компании и продукту. Презентовать уникальную строительную систему «Стронг»®, подробно рассказать о преимуществах работы с компанией «Стронг Билдингс». Заказчик хотел «продающий» сайт, который бы демонстрировал высокий уровень профессионализма команды, выглядел дорого, снимал большинство вопросов и успешно конвертировал посетителей в покупателей. В качестве референсов были указаны сайты ведущих иностранных компаний отрасли, сайты американских компаний с современным дизайном и достаточно агрессивным маркетингом.

Редизайн сайта:

I. Маркетинговый анализ:

1. Провели исследование рынка и потребностей целевой аудитории, разработали сценарии взаимодействия пользователя с сайтом.

2. Глубоко переработали структуру сайта: каталоги по типам зданий и готовых проектов, лендинги по типовым решениям, партнерский раздел — все элементы связаны общей логикой, ведущей посетителя к заполнению форм и отправке заявок.

3. Чтобы сократить время принятия решений, сделать сайт удобным и функциональным (и собирать контакты потенциальных клиентов) мы реализовали следующие сценарии:

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

— Запросить техзадание на индивидуальный проект здания (производственного, складского, офисного и т.д.) и получить базовый расчёт на электронную почту.

— Калькулятор для расчета базового серийного здания.

— Запрос проектной документации по типовым серийным зданиям.

— Формы: заказ обратного звонка с сайта, запрос бесплатной консультации, форма обратной связи, запрос условий партнерства и т.д

— Интерактивный чат с менеджером компании.

— Интерактивная карта с поиском партнеров по регионам.

4. Для каждой страницы сайта разработана адаптивная макетная сетка (в шести точках: 1920px, 1440px, 1280px, 768px, 540px, 320px), которая регламентирует размеры, назначение и требования к функциональности элементов.

II. Дизайн:

1) Редизайн логотипа, переработана цветовая гамма, изменены фирменные шрифты.

2) Разработан гайдлайн на 50 страницах — основные требования к типографике, инфографике (базовый набор иконок, требования к разработке иконок при расширении базового набора, правила использования табличной и цифровой информации, графики и диаграммы), разработан полный UI-kit для сайта.

3) Для презентации уникальной технологии строительства отрисованы 3D макеты зданий серии «Стронг»® с максимальной детализацией основных узлов.

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

III. Front-end, Back-end, заполнение контентом:

1) Сайт выполнен по индивидуальному проекту на CMS 1C-Bitrix.

2) Проведена адаптивная верстка всех страниц сайта (breackpoints: 1920px, 1440px, 1280px, 768px, 540px, 320px), отдельно создана собственная уникальная сетка сайта и её адаптация под основные разрешения экранов.

3) Разработаны анимационные элементы: 3d презентации зданий, анимация иконок, меню, диаграмм, уникальный слайдер, который конфигурируется под содержимое слайдов.

4) Выполнены интерактивные элементы: калькуляторы, формы заказа индивидуальных зданий, формы запроса данных, карта партнеров «Стронг Билдингс» и т. д.

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

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

7) Установлен и настроен виджет от Callibri, проведена техническая подготовка сайта для продвижения в поисковых системах.

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

________________________

We had to present a unique construction system “Strong”® and increase customer confidence in the company. The client ordered a site that looks expensive, answers visitors’ questions and sells well. Examples were the sites of the world’s leading industrial companies, American sites with modern design and aggressive marketing.

We did a lot of analytical work: we investigated user behavior and scenarios to solve user tasks, their main questions and objections. We have developed 3D models to present the construction technology “Strong”®. Each page should involve users in interaction with the site, increase online time and conversion of visitors to leads.

Website redesign:

Marketing analytics:

1. We did a lot of analytical work: conducted marketing research, studied the needs of the target audience, studied the behavior of users and interaction with the site, scenarios for solving their problems, the main questions and objections.

2 .We made site`s restructure: landings by standard solutions, catalogs by types of buildings and finished projects, partners` section — all elements are connected by a common logic leading the visitor to fill out forms and send applications.

3. In order to reduce decision-making time, make the site convenient and functional (and collect leads) we`ve implemented the following interactive scripts:

— Online Configurator calculates a typical project (or forms an individual project) and sends estimate docs to email for free.

— Request a technical specifications for an individual project (production, office building, warehouse, etc.) and get the basic calculation by email.

— Calculator for estimation of the basic serial building.

— Request design documentation for the serial model of the building.

— Forms: order a callback from the site, request a free consultation, feedback form, request the terms of partnership, etc.

— Interactive chat with the company manager.

— Interactive map with search for partners by region.

4. We developed an adaptive layout grid for each page (in six points: 1920px, 1440px, 1280px, 768px, 540px, 320px), which regulates the size, purpose and functionality requirements of the elements.

II. Design:

a) Logo redesign, change of the corporate fonts and the color scheme.

b) 50-page guideline for web — basic requirements for typography, infographics (basic icon set and specifications for expanded icon set, rules for tabs, graphs and diagrams making) and the complete UI-kit for the site.

c) 3D models of buildings of the «Strong»® series with maximum details of the main nodes to present unique construction technology.

d) about 60 adaptive graphic layouts of the site pages (in six points). It took about 5 iterations before the site acquired the final form: the customer carefully selected illustrations, texts, etc.

III. Front-end, Back-end, content:

1) Individual project on CMS 1C-Bitrix.

2) Adaptive page layout (breackpoints: 1920px, 1440px, 1280px, 768px, 540px, 320px), unique layout grid adapted to the main screen resolutions.

3) Animation elements: 3d presentations of buildings, animation of icons, menus, charts, slider customized to the content of the slides.

4) Interactive elements: calculators, individual buildings order forms, data request forms, Strong Buildings partners map, etc.

5) Modular structure for selling pages: landings are made of blocks, their order can be changed, enable and disable screens, flexibly customize the appearance of each landing.

6) Rewrited texts, the whole site translated into English, photos of all buildings updated, infographics of the system`s advantages made.

7) Callibri widget installed and configured, the site is technically prepared for promotion in search engines.

The site is bilingual, adapted to the English-speaking audience and search from abroad.

Скриншоты

Достигнутые цели и KPI по итогам запуска

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

Благодаря вовлечению пользователя в формирование заказа конверсия в заявку выросла на 40%.

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

Процент отказов снизился на 65%.

________________________

The site is functional and convenient: user can order online with a minimum of clarifications, all pages contain comprehensive information, high-quality illustrations, the text is clear and concise. The site stands out from the competition informative, visual, open publication of prices, you can calculate the cost of the building and find a reliable partner for construction in your region.

The users are involved in the order process, so the conversion has increased by 40%.

Thanks to the new website structure, the time visitors spent on the site has increased by 120%. The failure rate decreased by 65%.

Комментарий заказчика

Олег Ломакин, генеральный директор ООО «Стронг Билдингс»:

«Я доволен проектом: новый сайт «Стронг Билдингс» соответствует моим представлениям об эффективном корпоративном сайте и привлекает потенциальных клиентов.

Главное, что порадовало меня в работе компании VediTa — у команды есть свое видение проекта, вы помогаете найти оптимальное решение и грамотно выстраиваете работу с клиентом. Я готов рекомендовать компанию VediTa для выполнения сложных и качественных проектов».

_______________________________

Oleg Lomakin, CEO Strong Buildings, LLC:

«I like this project: new Strong Buildings site matches my ideas about an effective corporate website and attracts potential customers.

Most of all I liked that the VediTa team has its own vision of the project, helps you to find the optimal solution and competently build work with the client. I am ready to recommend VediTa for complex and high-quality projects».

python — Разделение бизнес-логики и доступа к данным в django

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

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

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

О доменной модели

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

Мышление в командах

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

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

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

Такой сценарий также действительно поможет вам в настройке среды разработки через тестирование.

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

Выражения команд

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

Сервисный уровень

Сервисный модуль уже был описан @Hedde . Здесь вы определяете отдельный модуль, и каждая команда представляется как функция.

services.py

def activate_user(user_id):
    user = User.objects.get(pk=user_id)

    # set active flag
    user. active = True
    user.save()

    # mail user
    send_mail(...)

    # etc etc

Использование форм

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

  • выполнение команды (что она делает?)
  • проверка параметров команды (можно ли это сделать?)
  • презентация команды (как я могу это сделать?)

forms.py

class ActivateUserForm(forms.Form):

    user_id = IntegerField(widget = UsernameSelectWidget, verbose_name="Select a user to activate")
    # the username select widget is not a standard Django widget, I just made it up

    def clean_user_id(self):
        user_id = self.cleaned_data['user_id']
        if User.objects.get(pk=user_id).active:
            raise ValidationError("This user cannot be activated")
        # you can also check authorizations etc. 
        return user_id

    def execute(self):
        """
        This is not a standard method in the forms API; it is intended to replace the 
        'extract-data-from-form-in-view-and-do-stuff' pattern by a more testable pattern.  
        """
        user_id = self.cleaned_data['user_id']

        user = User.objects.get(pk=user_id)

        # set active flag
        user.active = True
        user.save()

        # mail user
        send_mail(...)

        # etc etc

Мышление в запросах

Ваш пример не содержал никаких запросов, поэтому я позволил себе составить несколько полезных запросов. Я предпочитаю использовать термин «вопрос», но запросы — это классическая терминология. Интересные запросы: «Как зовут этого пользователя?», «Может ли этот пользователь войти в систему?», «Показать список деактивированных пользователей» и «Каково географическое распределение деактивированных пользователей?»

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

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

Другой вопрос: «Есть ли у меня полный контроль над ответами?» Например, при запросе имени пользователя (в этом контексте) мы не имеем никакого контроля над результатом, потому что мы полагаемся на внешний API.

Создание запросов

Самый простой запрос в Django — это использование объекта Manager:

User.objects.filter(active=True)

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

Пользовательские теги и фильтры

Первый вариант полезен для запросов, которые являются просто презентационными: пользовательские теги и шаблоны фильтров.

template.html

<h2>Welcome, {{ user|friendly_name }}</h2>

template_tags. py

@register.filter
def friendly_name(user):
    return remote_api.get_cached_name(user.id)

Методы запросов

Если ваш запрос не просто презентационный, вы можете добавить запросы в ваш services.py (если вы его используете) или ввести модуль query.py :

queries.py

def inactive_users():
    return User.objects.filter(active=False)


def users_called_publysher():
    for user in User.objects.all():
        if remote_api.get_cached_name(user.id) == "publysher":
            yield user 

Модели прокси

Прокси-модели очень полезны в контексте бизнес-логики и отчетности. Вы в основном определяете расширенное подмножество вашей модели. Вы можете переопределить базовый QuerySet менеджера, переопределив метод Manager.get_queryset() .

models.py

class InactiveUserManager(models.Manager):
    def get_queryset(self):
        query_set = super(InactiveUserManager, self). get_queryset()
        return query_set.filter(active=False)

class InactiveUser(User):
    """
    >>> for user in InactiveUser.objects.all():
    …        assert user.active is False 
    """

    objects = InactiveUserManager()
    class Meta:
        proxy = True

Модели запросов

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

models.py

class InactiveUserDistribution(models.Model):
    country = CharField(max_length=200)
    inactive_user_count = IntegerField(default=0)

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

forms.py

class ActivateUserForm(forms.Form):
    # see above

    def execute(self):
        # see above
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()

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

signals.py

user_activated = Signal(providing_args = ['user'])
user_deactivated = Signal(providing_args = ['user'])

forms.py

class ActivateUserForm(forms.Form):
    # see above

    def execute(self):
        # see above
        user_activated. send_robust(sender=self, user=user)

models.py

class InactiveUserDistribution(models.Model):
    # see above

@receiver(user_activated)
def on_user_activated(sender, **kwargs):
        user = kwargs['user']
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()

Держать его в чистоте

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

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

То же самое касается представлений (потому что представления часто страдают от одной и той же проблемы).

  • Мой взгляд активно управляет моделями баз данных? Вы должны извлечь команду.

Некоторые ссылки

документация Django: модели прокси

документация Django: сигналы

Архитектура: доменно-управляемый дизайн

Проектирование Сервисного Слоя и Логики Приложения — @emacsway’s blog

Эта статья посвящена вопросам управления Логикой Приложения и проектированию Сервисного Слоя (Service Layer), Use Case, CQRS, Event Sourcing, MVC и др.

Прежде чем копнуть вглубь, было бы неплохо разобраться с тем, что такое Логика Приложения (Application Logic) и чем она отличается от Бизнес-Логики (Business Logic).

Одно из наиболее часто-цитируемых определений основных концептуальных слоев дает Eric Evans:

User Interface (or Presentation Layer)
Responsible for showing information to the user and interpreting the user’s
commands. The external actor might sometimes be another computer
system rather than a human user.
Application Layer
Defines the jobs the software is supposed to do and directs the expressive
domain objects to work out problems. The tasks this layer is responsible
for are meaningful to the business or necessary for interaction with the
application layers of other systems.
This layer is kept thin. It does not contain business rules or knowledge, but
only coordinates tasks and delegates work to collaborations of domain
objects in the next layer down. It does not have state reflecting the
business situation, but it can have state that reflects the progress of a task
for the user or the program.
Domain Layer (or Model Layer)
Responsible for representing concepts of the business, information about
the business situation, and business rules. State that reflects the business
situation is controlled and used here, even though the technical details of
storing it are delegated to the infrastructure. This layer is the heart of
business software.
Infrastructure Layer
Provides generic technical capabilities that support the higher layers:
message sending for the application, persistence for the domain, drawing
widgets for the UI, and so on. The infrastructure layer may also support
the pattern of interactions between the four layers through an
architectural framework.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software” by Eric Evans

Ward Cunningham дает следующие определения:

Factor your application classes into four layers in the following way (see Figure 1: FourLayerArchitecture):

The View layer. This is the layer where the physical window and widget objects live. It may also contain Controller classes as in classical MVC. Any new user interface widgets developed for this application are put in this layer. In most cases today this layer is completely generated by a window-builder tool.

The ApplicationModel layer. This layer mediates between the various user interface components on a GUI screen and translates the messages that they understand into messages understood by the objects in the domain model. It is responsible for the flow of the application and controls navigation from window to window. This layer is often partially generated by a window-builder and partially coded by the developer.

The DomainModel layer. This is the layer where most objects found in an OO analysis and design will reside. Examples of the types of objects found in this layer may be Orders, Employees, Sensors, or whatever is appropriate to the problem domain.

The Infrastructure layer. This is where the objects that represent connections to entities outside the application (specifically those outside the object world) reside. Examples of objects in this layer would include SQLTables, 3270Terminals, SerialPorts, SQLBrokers and the like.

— Four Layer Architecture, Ward Cunningham

Но что означает сам термин Бизнес (Business)?
Непонимание этого термина часто приводит к серьезным проблемам проектирования.
В это трудно поверить, но большинство разработчиков, даже с многолетним стажем, этого не понимают, и полагают что это что-то связанное с финансами.

Самое авторитетное пояснение термина Business можно найти, как обычно, на сайте Ward Cunningham:

Software intersects with the Real World. Imagine that.

Там же можно найти и определение термина Business Rule:

A Business Rule (in a programming context) is knowledge that gets applied to a set of data to create new value. Or it may be a rule about how to create, modify, or remove data. Or perhaps it is a rule that specifies when certain processes occur.

For example, we have a rule about email addresses – when the Driver Name field on our object identifier changes, we erase the email address. When we receive a new email address, we make sure that it contains an “@” sign and a valid domain not on our blacklist.

Business Logic Definition:

Business logic is that portion of an enterprise system which determines how data is:

  • Transformed and/or calculated. For example, business logic determines how a tax total is calculated from invoice line items.
  • Routed to people or software systems, aka workflow.

Следует отличать термин Business (по сути — синоним слова Domain) от термина Business Domain:

A category about the business domain, such as accounting, finance, inventory, marketing, tracking, billing, reporting, charting, taxes, etc.

Также следует отличать Business и от Business Process:

A Business Process is some reproduceable process within an organization. Often it is a something that you want to setup once and reuse over and over again.

Companies spend a lot of time and money identifying Business Processes, designing the software that captures a Business Process and then testing and documenting these processes.

One example of a Business Process is “Take an order on my web site”. It might involve a customer, items from a catalog and a credit card. Each of these things is represented by business objects and together they represent a Business Process.

Википедия дает следующее определение термину Business Logic:

In computer software, business logic or domain logic is the part of the program that encodes the real-world Business Rules that determine how data can be created, stored, and changed. It is contrasted with the remainder of the software that might be concerned with lower-level details of managing a database or displaying the user interface, system infrastructure, or generally connecting various parts of the program.

И поясняет, чем отличается Business Logic от Business Rules:

Business logic should be distinguished from business rules.[“Definition of business logic“] Business logic is the portion of an enterprise system which determines how data is transformed or calculated, and how it is routed to people or software (workflow). Business rules are formal expressions of business policy. Anything that is a process or procedure is business logic, and anything that is neither a process nor a procedure is a business rule. Welcoming a new visitor is a process (workflow) consisting of steps to be taken, whereas saying every new visitor must be welcomed is a business rule. Further, business logic is procedural whereas business rules are declarative.[William Ulrich. “OMG Business Rules Symposium” (архив оригинала от 2013-12-24)]

Craig Larman считает термин Business синонимом к термину Domain, и в книге “Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development” он многократно приводит их рядом, дополняя один термин другим, взятым в скобки.
Термину Business Rules он дает следующее определение:

Business Rules — Business rules (also called Domain Rules) typically describe requirements or policies that transcend one software project — they are required in the domain or business, and many applications may need to conform to them. An excellent example is government tax laws. Domain rule details may be recorded in the Supplementary Specification, but because they are usually more enduring and applicable than for one software project, placing them in a central Business Rules artifact (shared by all analysts of the company) makes for better reuse of the analysis effort.

<…>

The Business Rules (or Domain Rules) capture long-living and spanning rules or policies, such as tax laws, that transcend one particular application.

<…>

Domain rules [Ross97, GK00] dictate how a domain or business may operate. They are not requirements of any one application, although an application’s requirements are often influenced by domain rules. Company policies, physical laws (such as how oil flows underground), and government laws are common domain rules.

They are commonly called business rules, which is the most common type, but that term is poor, as many software applications are for non-business problems, such as weather simulation or military logistics. A weather simulation has “domain rules,” related to physical laws and relationships, that influence the application requirements.

It’s useful to identify and record domain rules in a separate application-independent artifact — what the UP calls the Business Rules artifact — so that this analysis can be shared and reused across the organization and across projects, rather than buried within a project-specific document.

—“Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development” by Craig Larman

Резюмируя, я обобщу все своими словами:

Бизнес-Логика (деловые регламенты, доменные модели)
это моделирование объектов и процессов предметной области (т.е. реального мира).
Это то, что программа должна делать (от слова “дело” — именно так переводится слово “business”), и ради чего она создается.
Логика приложения
это то, что обеспечивает и координирует работу Бизнес-Логики.

Robert Martin в “Clean Architecture” подразделяет Бизнес-Правила на два вида:

Главы 16, 20 и 22 of Clean Architecture разъясняют в подробностях типы Бизнес-Правил.

При этом, Robert Martin выводит свои 4 слоя: Entities, Use Cases, Interface Adapters, Frameworks and Drivers.

Нужно отметить, что Robert Martin под “Business Rules” понимает не только правила, но и процедуры, смывая грань между “Business Rules” и “Business Logic”:

Строго говоря, бизнес-правила — это правила или процедуры, делающие или экономящие деньги.

Strictly speaking, business rules are rules or procedures that make or save the business money.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin, Chapter 20 “Business Rules”

При этом, у него можно наблюдать небольшое противоречие.
С одной стороны, вся суть “Business Rules” у него сводится к тому, что они относятся исключительно к реальному миру:

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

Банк взимает N% за кредит — это бизнес-правило, которое приносит банку деньги.
И неважно, имеется ли компьютерная программа, вычисляющая процент, или служащий вычисляет его на счетах.

Strictly speaking, business rules are rules or procedures that make or save the business money.
Very strictly speaking, these rules would make or save the business money, irrespective of whether they were implemented on a computer.
They would make or save money even if they were executed manually.

The fact that a bank charges N% interest for a loan is a business rule that makes the bank money.
It doesn’t matter if a computer program calculates the interest, or if a clerk with an abacus calculates the interest.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin, Chapter 20 “Business Rules”

Далее Robert Martin говорит важную информацию — “Business Rules” являются причиной существования Приложения.
Из этого следует, что Приложение уже не может являться причиной существования “Business Rules”:

Бизнес-правила являются причиной существования программной системы.
Они составляют основу функционирования.
Они порождают код, который делает или экономит деньги.
Они — наши семейные реликвии.

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

Business rules are the reason a software system exists.
They are the core functionality.
They carry the code that makes, or saves, money.
They are the family jewels.

The business rules should remain pristine, unsullied by baser concerns such as the user interface or database used.
Ideally, the code that represents the business rules should be the heart of the system, with lesser concerns being plugged in to them.
The business rules should be the most independent and reusable code in the system.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin, Chapter 20 “Business Rules”

Однако, с другой стороны, он допускает существование “Business Rules” в контексте функционирования приложения:

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

Not all business rules are as pure as Entities.
Some business rules make or save money for the business by defining and constraining the way that an automated system operates.
These rules would not be used in a manual environment, because they make sense only as part of an automated system.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin, Chapter 20 “Business Rules”

Не совсем понятно — “Business Rules” являются причиной существования Приложения, или имеют имеют смысл только как часть Приложения?
“Business rules are the reason a software system exists” или “they make sense only as part of an automated system”?

Тут просматривается небольшое взаимоисключение, и это именно та причина, по которой я придерживаюсь формулировки Eric Evans — “Application Layer does not contain business rules”.

Понятно, что здесь не хватает термина для выражения различных явлений, и Robert Martin решает дифференцировать уже существующий термин “Business Rules”, разделив его на два уровня — “Critical Business Rules” и “Application-specific Business Rules”:

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

A use case is a description of the way that an automated system is used.
It specifies the input to be provided by the user, the output to be returned to the user, and the processing steps involved in producing that output.
A use case describes application-specific business rules as opposed to the Critical Business Rules within the Entities.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin, Chapter 20 “Business Rules”

Но далее он сводит обязанности Use Case к обязанностям Application Logic, и подчеркивает, что Use Case координирует “Critical Business Rules”, реализованных в виде Entities:

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

<…>

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

Use cases contain the rules that specify how and when the Critical Business Rules within the Entities are invoked.
Use cases control the dance of the Entities.

<…>

Why are Entities high level and use cases lower level?
Because use cases are specific to a single application and, therefore, are closer to the inputs and outputs of that system.
Entities are generalizations that can be used in many different applications, so they are farther from the inputs and outputs of the system.
Use cases depend on Entities; Entities do not depend on use cases.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin, Chapter 20 “Business Rules”

Хотя, Robert Martin выделяет отдельную категорию классов UseCase (Interactor) для Application-specific Business Rules, на практике этот уровень часто “округляется” до уровня Application Logic.
Так, например, Martin Fowler и Randy Stafford разделяют “Business Logic” на два вида — Логика Домена (Domain Logic) и Логика Приложения (Application Logic):

Подобно сценарию транзакции (Transaction Script, 133) и модели предметной области
(Domain Model, 140), слой служб представляет собой типовое решение по организации
бизнес-логики. Многие проектировщики, и я в том числе, любят разносить бизнес-логику
по двум категориям: логика домена (domain logic) имеет дело только с предметной
областью как таковой (примером могут служить стратегии вычисления зачтенного дохода
по контракту), а логика приложения (application logic) описывает сферу ответственности
приложения [11] (скажем, уведомляет пользователей и сторонние приложения о протекании
процесса вычисления доходов). Логику приложения часто называют также
“логикой рабочего процесса”, несмотря на то что под “рабочим процессом” часто понимаются
совершенно разные вещи.

Like Transaction Script (110) and Domain Model (116), Service Layer is a pattern for organizing business logic.
Many designers, including me, like to divide “business logic” into two kinds: “domain logic,” having to
do purely with the problem domain (such as strategies for calculating revenue recognition on a contract), and
“application logic,” having to do with application responsibilities [Cockburn UC] (such as notifying contract
administrators, and integrated applications, of revenue recognition calculations). Application logic is
sometimes referred to as “workflow logic,” although different people have different interpretations of
“workflow.”

— “Patterns of Enterprise Application Architecture” by Martin Fowler, Randy Stafford

Местами он склонен относить “Business Rules” к Доменой Логике (Domain Logic):

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

The problem came with domain logic: business rules, validations, calculations, and the like.

— “Patterns of Enterprise Application Architecture” by Martin Fowler

И даже признает наличие определенной расплывчатости:

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

Then there’s the matter of what comes under the term “business logic.”
I find this a curious term because there are few things that are less logical than business logic.

— “Patterns of Enterprise Application Architecture” by Martin Fowler

Поскольку целью создания приложения является реализация именно Business Logic — критически важно обеспечить их переносимость, и отделить их от Application Logic.
Эти два вида логики будут изменяться в разное время, с разной частотой и по разным причинам, поэтому их следует разделить
так, чтобы их можно было изменять независимо .
В свое время Гради Буч сказал, что “Архитектура отражает важные проектные решения по формированию системы, где важность определяется стоимостью изменений” .

Широко распространены четыре способа организации Логики Приложения (Application Logic):

  1. Оркестровый Сервис (“request/response”, т.е. сервис осведомлен об интерфейсе других сервисов), он же — Сервисный Слой (Service Layer).
  2. Хореографический Сервис (Event-Driven, т.е. loosely coupled), который является разновидностью паттерна Command, и используется, как правило, в Event-Driven Architecture (в частности, в CQRS и Event Sourcing приложениях, наглядный пример — reducer в Redux), и в DDD-приложениях (обработчик Domain/Integration Event).
  3. Front Controller и Application Controller (которые тоже, по сути, является разновидностью паттерна Command).

“A Front Controller handles all calls for a Web site, and is usually structured in two parts: a Web handler and a command hierarchy.”

— “Patterns of Enterprise Application Architecture” by Martin Fowler and others.

“For both the domain commands and the view, the application controller needs a way to store something it can invoke.
A Command [Gang of Four] is a good choice, since it allows it to easily get hold of and run a block of code.”

— “Patterns of Enterprise Application Architecture” by Martin Fowler and others.

4. Use Case (см. также), который также, является разновидностью паттерна Command.
На 15:50 Robert C. Martin проводит параллель между Use Case и паттерном Command.

Собственно говоря, производной паттерна Command является даже Method Object.

Use Case обязан своим существованием именно наличию application-specific Business Rules, которые не имеют смысла существования вне контекста приложения.
Он обеспечивает их независимость от приложения путем инверсии контроля (IoC).

Если бы Use Case не содержал Бизнес-Логики, то не было бы и смысла отделять его от Page Controller, иначе приложение пыталось бы абстрагироваться от самого себя же.

Мы видим, что в организации Логики Приложения широко применяются разновидности паттерна Команда (Command).

Рассмотренные способы организовывают, в первую очередь, Логику Приложения, и лишь во вторую очередь, Бизнес-Логику, которая не обязательно должна присутствовать, кроме случая использования Use Case, т.к. иначе он утратил бы причины для существования.

При правильной организации Бизнес-Логики, и высоком качестве ORM (в случае его использования, конечно же), зависимость Бизнес-Логики от приложения будет минимальна.
Основная сложность любого ORM заключается в том, чтобы организовать доступ к связанным объектам не подмешивая Логику Приложения (и логику доступа к данным) в Domain Models, — эту тему мы подробно рассмотрим в одном из следующих постов.

Понимание общих признаков в способах управления Логикой Приложения позволяет проектировать более гибкие приложения, и, как результат, более безболезненно заменять архитектурный шаблон, например, из Layered в Event-Driven.
Частично эта тема затрагивается в Chapter 16 “Independence” of “Clean Architecture” by Robert C. Martin и в разделе “Premature Decomposition” of Chapter 3 “How to Model Services” of “Building Microservices” by Sam Newman.

SERVICE — An operation offered as an interface that stands alone in the model, with no encapsulated state.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software”

In some cases, the clearest and most pragmatic design includes operations that do not
conceptually belong to any object. Rather than force the issue, we can follow the natural contours
of the problem space and include SERVICES explicitly in the model.

There are important domain operations that can’t find a natural home in an ENTITY or VALUE
OBJECT . Some of these are intrinsically activities or actions, not things, but since our modeling
paradigm is objects, we try to fit them into objects anyway…

A SERVICE is an operation offered as an interface that stands alone in the model, without
encapsulating state, as ENTITIES and VALUE OBJECTS do. S ERVICES are a common pattern in technical
frameworks, but they can also apply in the domain layer.

The name service emphasizes the relationship with other objects. Unlike ENTITIES and VALUE
OBJECTS , it is defined purely in terms of what it can do for a client. A SERVICE tends to be named for
an activity, rather than an entity—a verb rather than a noun. A SERVICE can still have an abstract,
intentional definition; it just has a different flavor than the definition of an object. A SERVICE should
still have a defined responsibility, and that responsibility and the interface fulfilling it should be
defined as part of the domain model. Operation names should come from the UBIQUITOUS
LANGUAGE or be introduced into it. Parameters and results should be domain objects.

SERVICES should be used judiciously and not allowed to strip the ENTITIES and VALUE OBJECTS of all
their behavior. But when an operation is actually an important domain concept, a SERVICE forms a
natural part of a MODEL-DRIVEN DESIGN . Declared in the model as a SERVICE, rather than as a
phony object that doesn’t actually represent anything, the standalone operation will not mislead
anyone.

A good SERVICE has three characteristics.

1. The operation relates to a domain concept that is not a natural part of an ENTITY or VALUE
OBJECT .
2. The interface is defined in terms of other elements of the domain model.
3. The operation is stateless.

Statelessness here means that any client can use any instance of a particular SERVICE without
regard to the instance’s individual history. The execution of a SERVICE will use information that is
accessible globally, and may even change that global information (that is, it may have side
effects). But the SERVICE does not hold state of its own that affects its own behavior, as most
domain objects do.

When a significant process or transformation in the domain is not a natural
responsibility of an ENTITY or VALUE OBJECT, add an operation to the model as a
standalone interface declared as a SERVICE. Define the interface in terms of the
language of the model and make sure the operation name is part of the UBIQUITOUS
LANGUAGE. Make the SERVICE stateless.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software”

Eric Evans разделяет Сервисы на три уровня логики:

Partitioning Services into Layers

Application

Funds Transfer App Service

  • Digests input (such as an XML request).
  • Sends message to domain service for fulfillment.
  • Listens for confirmation.
  • Decides to send notification using infrastructure service.
Domain

Funds Transfer Domain Service

  • Interacts with necessary Account and Ledger objects, making appropriate debits and credits.
  • Supplies confirmation of result (transfer allowed or not, and so on).
Infrastructure Send Notification Service
Sends e-mails, letters, and other communications as directed by the application.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software”

Most SERVICES discussed in the literature are purely technical and belong in the infrastructure layer.
Domain and application SERVICES collaborate with these infrastructure SERVICES.
For example, a bank might have an application that sends an e-mail to a customer when an account balance falls below a specific threshold.
The interface that encapsulates the e-mail system, and perhaps alternate means of notification, is a SERVICE in the infrastructure layer.

It can be harder to distinguish application SERVICES from domain SERVICES.
The application layer is responsible for ordering the notification.
The domain layer is responsible for determining if a threshold was met—though this task probably does not call for a SERVICE, because it would fit the responsibility of an “account” object.
That banking application could be responsible for funds transfers.
If a SERVICE were devised to make appropriate debits and credits for a funds transfer,that capability would belong in the domain layer.
Funds transfer has a meaning in the banking domain language, and it involves fundamental business logic.
Technical SERVICES should lack any business meaning at all.

Many domain or application SERVICES are built on top of the populations of ENTITIES and VALUES, behaving like scripts that organize the potential of the domain to actually get something done.
ENTITIES and VALUE OBJECTS are often too fine-grained to provide a convenient access to the capabilities of the domain layer.
Here we encounter a very fine line between the domain layer and the application layer.
For example, if the banking application can convert and export our transactions into a spreadsheet file for us to analyze, that export is an application SERVICE.
There is no meaning of “file formats” in the domain of banking, and there are no business rules involved.

On the other hand, a feature that can transfer funds from one account to another is a domain SERVICE because it embeds significant business rules (crediting and debiting the appropriate accounts, for example) and because a “funds transfer” is a meaningful banking term.
In this case, the SERVICE does not do much on its own; it would ask the two Account objects to do most of the work.
But to put the “transfer” operation on the Account object would be awkward, because the operation involves two accounts and some global rules.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software”

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

Domain Models (116) are preferable to Transaction Scripts (110) for avoiding domain logic duplication and
for managing complexity using classical design patterns.
But putting application logic into pure domain object classes has a couple of undesirable consequences.
First, domain object classes are less reusable across applications if they implement application-specific logic and depend on application-specific packages.
Second, commingling both kinds of logic in the same classes makes it harder to reimplement the application
logic in, say, a workflow tool if that should ever become desirable.
For these reasons Service Layer factors each kind of business logic into a separate layer, yielding the usual benefits of layering and rendering the pure domain object classes more reusable from application to application.

— “Patterns of Enterprise Application Architecture”

Политика самого высокого уровня принадлежит Доменной Логике (Domain Logic), поэтому, с нее и начнем.
К счастью, это самый немногочисленный представитель Сервисов.

Подробно тему Сервисов Логики Предметной Области и причины их существования раскрывает Vaughn Vernon:

Further, don’t confuse a Domain Service with an Application Service.
We don’t want to house business logic in an Application Service, but we do want business logic housed in a Domain Service.
If you are confused about the difference, compare with Application.
Briefly, to differentiate the two, an Application Service, being the natural client of the domain model, would normally be the client of a Domain Service.
You’ll see that demonstrated later in the chapter.
Just because a Domain Service has the word service in its name does not mean that it is required to be a coarse-grained, remote-capable, heavyweight transactional operation.

You can use a Domain Service to

  • Perform a significant business process
  • Transform a domain object from one composition to another
  • Calculate a Value requiring input from more than one domain object

— “Implementing Domain-Driven Design” by Vaughn Vernon

Это самый многочисленный представитель Сервисов.
Именно его часто называют Сервисный Слой (Service Layer).

Отдельно следует выделять Сервисы уровня Инфраструктурного Слоя (Infrastructure Layer).

The infrastructure layer usually does not initiate action in the domain layer. Being “below” the
domain layer, it should have no specific knowledge of the domain it is serving. Indeed, such
technical capabilities are most often offered as SERVICES . For example, if an application needs to
send an e-mail, some message-sending interface can be located in the infrastructure layer and the
application layer elements can request the transmission of the message. This decoupling gives
some extra versatility. The message-sending interface might be connected to an e-mail sender, a
fax sender, or whatever else is available. But the main benefit is simplifying the application layer,
keeping it narrowly focused on its job: knowing when to send a message, but not burdened with
how.

The application and domain layers call on the SERVICES provided by the infrastructure layer. When
the scope of a SERVICE has been well chosen and its interface well designed, the caller can remain
loosely coupled and uncomplicated by the elaborate behavior the SERVICE interface encapsulates.

But not all infrastructure comes in the form of SERVICES callable from the higher layers. Some
technical components are designed to directly support the basic functions of other layers (such as
providing an abstract base class for all domain objects) and provide the mechanisms for them to
relate (such as implementations of MVC and the like). Such an “architectural framework” has
much more impact on the design of the other parts of the program.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software”

Infrastructure Layer — Provides generic technical capabilities that support the higher layers:
message sending for the application, persistence for the domain, drawing
widgets for the UI, and so on. The infrastructure layer may also support
the pattern of interactions between the four layers through an
architectural framework.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software”

По способу взаимодействия Сервисы разделяются на Оркестровые (“request/response”, т.е. сервис осведомлен об интерфейсе других сервисов) и Хореографические (Event-Driven, т.е. loosely coupled) .
Их еще называют идиоматическими стилями взаимодействия.
Главный недостаток первого — это высокая осведомленность об интерфейсе других Сервисов, т.е. Высокое Сопряжение (High Coupling), что снижает их реиспользование.
Последний же является разновидностью паттерна Command, и используется, как правило, в Event-Driven Architecture (в частности, в CQRS и Event Sourcing приложениях, наглядный пример — reducer в Redux), и в DDD-приложениях (обработчик Domain/Integration Event).

With orchestration, we rely on a central brain to guide and drive the process, much like the conductor in an orchestra. With choreography, we inform each part of the system of its job, and let it work out the details, like dancers all finding their way and reacting to others around them in a ballet.

<…>

The downside to this orchestration approach is that the customer service can become too much of a central governing authority. It can become the hub in the middle of a web, and a central point where logic starts to live.
I have seen this approach result in a small number of smart “god” services telling anemic CRUD-based services what to do.

With a choreographed approach, we could instead just have the customer service emit an event in an asynchronous manner, saying Customer created.
The email service, postal service, and loyalty points bank then just subscribe to these events and react accordingly, as in Figure 4-4.
This approach is significantly more decoupled.
If some other service needed to reach to the creation of a customer, it just needs to subscribe to the events and do its job when needed.
The downside is that the explicit view of the business process we see in Figure 4-2 is now only implicitly reflected in our system.

— “Building Microservices. Designing Fine-Grained Systems” by Sam Newman

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

Существует интересная статья “Clarified CQRS” by Udi Dahan, на которую ссылается Martin Fowler в своей статье “CQRS”.

И в этой статье есть интересный момент.

The reason you don’t see this layer explicitly represented in CQRS is that it isn’t really there…

— “Clarified CQRS” by Udi Dahan

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

Our command processing objects in the various autonomous components actually make up our service layer.

— “Clarified CQRS” by Udi Dahan

Хореографические Сервисы бывают только уровня Логики Приложения, даже если они подписаны на Доменные События (Domain Event).

Частые ошибки проектирования Хореографических Сервисов

Иногда, особенно у frontend-разработчиков, можно наблюдать как они проксируют Оркестровыми Сервисами обращения к Хореографическим Сервисам.
Часто это происходит при использовании Redux/NgRx в Angular-приложении, в котором широко используются Сервисы.
Имея слабо-сопряженные (Low Coupling) событийно-ориентированные Сервисы в виде обработчиков команды, было бы проектной ошибкой пытаться связать их в сильно-зацепленные (High Coupling) классические Сервисы Оркестрового типа (с единственной целью — помочь Логике Приложения скрыть их от самой же себя).

Each command is independent of the other, so why should we allow the objects which handle them to depend on each other?

— “Clarified CQRS” by Udi Dahan

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

Другой распространенной ошибкой является размещение Бизнес-Логики в Хореографических Сервисах и искусственное вырождение поведения Доменных Моделей с выносом всей бизнес-логики в обработчики команд, т.е. в Сервисы.

Это приводит к появлению проблемы, о которой говорил Eric Evans:

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

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

“If the framework’s partitioning conventions pull apart the elements implementing the
conceptual objects, the code no longer reveals the model.

There is only so much partitioning a mind can stitch back together, and if the framework uses
it all up, the domain developers lose their ability to chunk the model into meaningful pieces.”

— “Domain-Driven Design: Tackling Complexity in the Heart of Software” by Eric Evans

В приложениях с обширной бизнес-логикой это может сильно ухудшить качество бизнес-моделирования, и препятствовать процессу дистилляции моделей по мере переработки бизнес-знаний .
Также такой код обретает признаки “Divergent Change” и “Shotgun Surgery” , что сильно затрудняет исправление ошибок бизнес-моделирования и Итерационное Проектирование (Evolutionary Design).
В конечном итоге это приводит к стремительному росту стоимости изменения программы.

Должен заметить, что Udi Dahan в своей статье допускает и использование Transaction Script для организации бизнес-логики.
В таком случае, выбор между Transaction Script и Domain Model подробно рассмотрен в “Patterns of Enterprise Application Architecture” by M. Fowler and others.
Transaction Script может быть уместным при сочетании Redux и GraphQL для минимизации сетевого трафика.
При использовании же REST-API, и наличии обширной бизнес-логики, более уместным будет использование Domain Model и DDD.

По способу обмена данными Сервисы разделяются на Синхронные и Асинхронные.

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

Классы UseCases/Interactors являются разновидностью паттерна Команда (Command), и, в определенной мере, могут рассматриваться как Statefull Сервис.

Похожую идею выражает и Eric Evans:

We might like to create a Funds Transfer object to represent the two entries plus the rules and history around the transfer. But we are still left with calls to SERVICES in the interbank networks.
What’s more, in most development systems, it is awkward to make a direct interface between a domain object and external resources. We can dress up such external SERVICES with a FACADE that takes inputs in terms of the model, perhaps returning a Funds Transfer object as its result.
But whatever intermediaries we might have, and even though they don’t belong to us, those SERVICES are carrying out the domain responsibility of funds transfer.

— “Domain-Driven Design: Tackling Complexity in the Heart of Software”

И Randy Stafford с Martin Fowler:

Двумя базовыми вариантами реализации слоя служб являются создание интерфейса
доступа к домену (domain facade) и конструирование сценария операции (operation script).
При использовании подхода, связанного с интерфейсом доступа к домену, слой служб
реализуется как набор “тонких” интерфейсов, размещенных “поверх” модели предметной
области. В классах, реализующих интерфейсы, никакая бизнес-логика отражения не
находит — она сосредоточена исключительно в контексте модели предметной области.
Тонкие интерфейсы устанавливают границы и определяют множество операций, посредством
которых клиентские слои взаимодействуют с приложением, обнаруживая тем самым
характерные свойства слоя служб.

Создавая сценарий операции, вы реализуете слой служб как множество более “толстых”
классов, которые непосредственно воплощают в себе логику приложения, но за бизнес-логикой
обращаются к классам домена. Операции, предоставляемые клиентам слоя
служб, реализуются в виде сценариев, создаваемых группами в контексте классов, каждый
из которых определяет некоторый фрагмент соответствующей логики. Подобные
классы, расширяющие супертип слоя (Layer Supertype, 491) и уточняющие объявленные
в нем абстрактные характеристики поведения и сферы ответственности, формируют “службы”
приложения (в названиях служебных типов принято употреблять суффикс “Service”).
Слой служб и заключает в себе эти прикладные классы.

The two basic implementation variations are the domain facade approach and the operation script approach. In
the domain facade approach a Service Layer is implemented as a set of thin facades over a Domain Model
(116). The classes implementing the facades don’t implement any business logic. Rather, the Domain Model
(116) implements all of the business logic. The thin facades establish a boundary and set of operations through
which client layers interact with the application, exhibiting the defining characteristics of Service Layer.

In the operation script approach a Service Layer is implemented as a set of thicker classes that directly
implement application logic but delegate to encapsulated domain object classes for domain logic. The
operations available to clients of a Service Layer are implemented as scripts, organized several to a class
defining a subject area of related logic. Each such class forms an application “service,” and it’s common for
service type names to end with “Service.” A Service Layer is comprised of these application service classes,
which should extend a Layer Supertype (475), abstracting their responsibilities and common behaviors.

— “Patterns of Enterprise Application Architecture” by Martin Fowler, Randy Stafford

Обратите внимание на использование термина “Domain Model”.
Эти ребята — последние из числа тех, кто может спутать “Domain Model” и “DataMapper”, особенно, при таком количестве редакторов и рецензентов.
Т.е. клиент ожидает от доменной модели интерфейс, который она, по какой-то причине (обычно это Single Responsibility Principle), не реализует и не должна реализовать.
С другой стороны, клиент не может реализовать это поведение сам, так как это привело бы к появлению “G14: Feature Envy” .
Для выравнивания интерфейсов служит паттерн Adapter (aka Wrapper), см. “Design Patterns Elements of Reusable Object-Oriented Software” .
Отличается Statefull Services от обычного Adapter только тем, что он содержит логику более низкого уровня, т.е. Логику Приложения (Application Logic), нежели Доменная Модель.

Этот подход сильно напоминает мне “Cross-Cutting Concerns” с тем только отличием, что “Cross-Cutting Concerns” реализует интерфейс оригинального объекта, в то время как domain facade дополняет его.
Когда объект-обертка реализует интерфейс оригинального объекта, то его обычно называют Aspect или Decorator.
Часто в таких случаях можно услышать термин Proxy, но, на самом деле паттерн Proxy имеет немного другое назначение.
Такой подход часто используется для того, чтобы наделить Доменную Модель логикой доступа к связанным объектам, при этом сохраняя Доменную Модель совершенно “чистой”, т.е. отделенной от поведения логики более низкого уровня.

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

Слой служб устанавливает множество доступных действий и координирует отклик приложения на каждое действие.

A Service Layer defines an application’s boundary with a layer of services that establishes a set of available
operations and coordinates the application’s response in each operation.

— “Patterns of Enterprise Application Architecture”

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

Слой служб определяет границы приложения и множество операций, предоставляемых им для интерфейсных клиентских слоев кода.
Он инкапсулирует бизнес-логику приложения, управляет транзакциями и координирует реакции на действия.

Enterprise applications typically require different kinds of interfaces to the data they store and the logic they implement: data loaders, user interfaces, integration gateways, and others.
Despite their different purposes, these interfaces often need common interactions with the application to access and manipulate its data and invoke its business logic.
The interactions may be complex, involving transactions across multiple resources and the coordination of several responses to an action.
Encoding the logic of the interactions separately in each interface causes a lot of duplication.

A Service Layer defines an application’s boundary and its set of available operations from the perspective of interfacing client layers.
It encapsulates the application’s business logic, controlling transactions and coordinating responses in the implementation of its operations.

— “Patterns of Enterprise Application Architecture”

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

The benefit of Service Layer is that it defines a common set of application operations available to many kinds
of clients and it coordinates an application’s response in each operation. The response may involve application
logic that needs to be transacted atomically across multiple transactional resources. Thus, in an application
with more than one kind of client of its business logic, and complex responses in its use cases involving
multiple transactional resources, it makes a lot of sense to include a Service Layer with container-managed
transactions, even in an undistributed architecture.

— “Patterns of Enterprise Application Architecture”

Один из общих подходов к реализации бизнес-логики состоит в расщеплении слоя
предметной области на два самостоятельных слоя: “поверх” модели предметной области
или модуля таблицы располагается слой служб (Service Layer, 156). Обычно это целесообразно
только при использовании модели предметной области или модуля таблицы, поскольку
слой домена, включающий лишь сценарий транзакции, не настолько сложен,
чтобы заслужить право на создание дополнительного слоя. Логика слоя представления
взаимодействует с бизнес-логикой исключительно при посредничестве слоя служб, который
действует как API приложения.

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

A common approach in handling domain logic is to split the domain layer in two. A Service Layer (133) is
placed over an underlying Domain Model (116) or Table Module (125). Usually you only get this with a
Domain Model (116) or Table Module (125) since a domain layer that uses only Transaction Script (110) isn’t
complex enough to warrant a separate layer. The presentation logic interacts with the domain purely through
the Service Layer (133), which acts as an API for the application.

As well as providing a clear API, the Service Layer (133) is also a good spot to place such things as
transaction control and security. This gives you a simple model of taking each method in the Service Layer
(133) and describing its transactional and security characteristics. A separate properties file is a common
choice for this, but .NET’s attributes provide a nice way of doing it directly in the code.

— “Patterns of Enterprise Application Architecture”

Традиционно Сервисный Слой относится к логике уровня Приложения.
Т.е. Сервисный Слой имеет более низкий уровень, чем слой предметной области (domain logic), именуемый так же деловыми регламентами (business rules).
Из этого также следует и то, что объекты предметной области не должны быть осведомлены о наличии Сервисного Слоя.

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

  • Компоновки атомарных операций (например, требуется одновременно сохранить данные в БД, редисе, и на файловой системе, в рамках одной бизнес-транзакции, или откатить все назад).
  • Сокрытия источника данных (здесь он дублирует функции паттерна Repository) и может быть опущен, если нет других причин.
  • Компоновки реиспользуемых операций уровня приложения (например, некая часть логики уровня приложения используется в нескольких различных контроллерах).
  • Как основа для реализации Интерфейса удаленного доступа.
  • Когда контроллер имеет какой-то большой метод, он нуждается в декомпозиции, и к нему применяется Extract Method для вычленения обязанностей в отдельные методы. При этом растет количество методов класса, что влечет за собой падение его сфокусированности или Связанности (т.е. коэффициент совместного использования свойств класса его методами). Чтобы восстановить связанность, эти методы выделяются в отдельный класс, образуя Method Object. И вот этот метод-объект и может быть преобразован в сервисный слой.
  • Сервисный слой можно использовать в качестве концентратора запросов, если он стоит поверх паттерна Repository и использует паттерн Query object. Дело в том, что паттерн Repository ограничивает свой интерфейс посредством интерфейса Query Object. А так как класс не должен делать предположений о своих клиентах, то накапливать предустановленные запросы в классе Repository нельзя, ибо он не может владеть потребностями всех клиентов. Клиенты должны сами заботиться о себе. А сервисный слой как раз и создан для обслуживания клиентов.

В остальных случаях логику сервисного слоя можно размещать прямо на уровне приложения (обычно — контроллер).

Гораздо легче ответить на вопрос, когда слой служб не нужно использовать. Скорее
всего, вам не понадобится слой служб, если у логики приложения есть только одна категория
клиентов, например пользовательский интерфейс, отклики которого на варианты
использования не охватывают несколько ресурсов транзакций. В этом случае управление
транзакциями и выбор откликов можно возложить на контроллеры страниц (Page
Controller, 350), которые будут обращаться непосредственно к слою источника данных.
Тем не менее, как только у вас появится вторая категория клиентов или начнет
использоваться второй ресурс транзакции, вам неизбежно придется ввести слой служб, что
потребует полной переработки приложения.

The easier question to answer is probably when not to use it. You probably don’t need a Service Layer if your
application’s business logic will only have one kind of client say, a user interface and its use case responses
don’t involve multiple transactional resources. In this case your Page Controllers can manually control
transactions and coordinate whatever response is required, perhaps delegating directly to the Data Source
layer.
But as soon as you envision a second kind of client, or a second transactional resource in use case responses, it
pays to design in a Service Layer from the beginning.

— “Patterns of Enterprise Application Architecture”

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

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

My preference is thus to have the thinnest Service Layer (133) you can, if you even need one. My usual
approach is to assume that I don’t need one and only add it if it seems that the application needs it. However, I
know many good designers who always use a Service Layer (133) with a fair bit of logic, so feel free to ignore
me on this one.

— “Patterns of Enterprise Application Architecture”

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

The idea of splitting a services layer from a domain layer is based on a separation of workflow logic from
pure domain logic. The services layer typically includes logic that’s particular to a single use case and also
some communication with other infrastructures, such as messaging. Whether to have separate services and
domain layers is a matter some debate. I tend to look as it as occasionally useful rather than mandatory, but
designers I respect disagree with me on this.

— “Patterns of Enterprise Application Architecture”

Часто Service Layer ошибочно делают как враппер над DataMapper.
Это не совсем верно.
Data Mapper обслуживает одну Domain Model (модель предметной области), Repository обслуживает один Aggregate , а Cервис обслуживает клиента (или группу клиентов).
Сервисный слой может манипулировать в рамках бизнес-транзакции или в интересах клиента несколькими мапперами и другими сервисами.
Поэтому методы сервиса обычно содержат имя возвращаемой Модели Домена в качестве суффикса (например, getUser()), в то время как методы Маппера и Хранилища в этом суффиксе не нуждается (так как имя МОдели Домена уже и так присутствует в имени класса Маппера, и Маппер обслуживает только одну Модель Домена).

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

Identifying the operations needed on a Service Layer boundary is pretty straightforward. They’re determined
by the needs of Service Layer clients, the most significant (and first) of which is typically a user interface.
(“Patterns of Enterprise Application Architecture” )

Некоторые примеры реализации:

Используйте инверсию управления, желательно в виде “Пассивного внедрения зависимостей” , Dependency Injection (DI).

Истинное внедрение зависимостей идет еще на один шаг вперед. Класс не
предпринимает непосредственных действий по разрешению своих зависимостей;
он остается абсолютно пассивным. Вместо этого он предоставляет set-методы
и/или аргументы конструктора, используемые для внедрения зависимостей.
В процессе конструирования контейнер DI создает экземпляры необходимых
объектов (обычно по требованию) и использует аргументы конструктора или
set-методы для скрепления зависимостей. Фактически используемые
зависимые объекты задаются в конфигурационном файле или на программном уровне
в специализированном конструирующем модуле.

True Dependency Injection goes one step further. The class takes no direct steps to
resolve its dependencies; it is completely passive. Instead, it provides setter methods or
constructor arguments (or both) that are used to inject the dependencies. During the con-
struction process, the DI container instantiates the required objects (usually on demand)
and uses the constructor arguments or setter methods provided to wire together the depen-
dencies. Which dependent objects are actually used is specified through a configuration
file or programmatically in a special-purpose construction module.
“Clean Code: A Handbook of Agile Software Craftsmanship”

Одна из основных обязанностей Сервисного Слоя — это сокрытие источника данных.
Для тестирования можно использовать фиктивный Сервис (Service Stub).
Этот же прием можно использовать для параллельной разработки, когда реализация сервисного слоя еще не готова.
Иногда бывает полезно подменить Сервис генератором фэйковых данных.
В общем, пользы от сервисного слоя будет мало, если нет возможности его подменить (или подменить используемые им зависимости).

Широко распространенная ошибка — использование класса django.db.models.Manager (а то и django.db.models.Model) в качестве сервисного слоя.
Нередко можно встретить, как какой-то метод класса django.db.models.Model принимает в качестве аргумента объект HTTP-запроса django.http.request.HttpRequest, например, для проверки прав.

Объект HTTP-запроса — это логика уровня приложения (application), в то время как класс модели — это логика уровня предметной области (domain), т.е. объекты реального мира, которую также называют правилами делового регламента (business rules).
Проверка прав — это тоже логика уровня приложения.

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

Классу django.db.models.Manager более всего соответствует класс Finder описанный в “Patterns of Enterprise Application Architecture” .

При реализации шлюза записи данных возникает вопрос: куда “пристроить” методы
поиска, генерирующие экземпляр данного типового решения? Разумеется, можно
воспользоваться статическими методами поиска, однако они исключают возможность
полиморфизма (что могло бы пригодиться, если понадобится определить разные методы
поиска для различных источников данных). В подобной ситуации часто имеет смысл
создать отдельные объекты поиска, чтобы у каждой таблицы реляционной базы данных
был один класс для проведения поиска и один класс шлюза для сохранения результатов
этого поиска.

Иногда шлюз записи данных трудно отличить от активной записи (Active Record, 182).
В этом случае следует обратить внимание на наличие какой-либо логики домена; если
она есть, значит, это активная запись. Реализация шлюза записи данных должна включать
в себя только логику доступа к базе данных и никакой логики домена.

With a Row Data Gateway you’re faced with the questions of where to put the find operations that generate this
pattern. You can use static find methods, but they preclude polymorphism should you want to substitute
different finder methods for different data sources. In this case it often makes sense to have separate finder
objects so that each table in a relational database will have one finder class and one gateway class for the results.

It’s often hard to tell the difference between a Row Data Gateway and an Active Record (160). The crux of the
matter is whether there’s any domain logic present; if there is, you have an Active Record (160). A Row Data
Gateway should contain only database access logic and no domain logic.
(Chapter 10. “Data Source Architectural Patterns : Row Data Gateway”, “Patterns of Enterprise Application Architecture” )

Хотя Django не использует паттерн Repository, она использует абстракцию критериев выборки, своего рода разновидность паттерна Query Object.
Подобно паттерну Repository, класс модели (ActiveRecord) ограничивает свой интерфейс посредством интерфейса Query Object.
Клиенты должны пользоваться предоставленным интерфейсом, а не возлагать на модель и ее менеджер свои обязанности по знанию своих запросов.
А так как никакой класс не должен делать предположений о своих клиентах, то накапливать предустановленные запросы в классе модели нельзя, ибо он не может владеть потребностями всех клиентов.
Клиенты должны сами заботиться о себе.
А сервисный слой как раз и создан для обслуживания клиентов.

Попытки исключить Сервинсый Слой из Django-приложений приводит к появлению менеджеров с огромным количеством методов.

Хорошей практикой было бы сокрытие посредством сервисного слоя способа реализации Django Models в виде ActiveRecord.
Это позволит безболезненно подменить ORM в случае необходимости.

Можно было бы поспорить и о размещении логики приложения. Думаю, некоторые
предпочли бы реализовать ее в методах объектов домена, таких, как
Contract. calculateRevenueRecognitions (), ИЛИ вообще В слое источника данных, ЧТО
позволило бы обойтись без отдельного слоя служб. Тем не менее подобное размещение
логики приложения кажется мне весьма нежелательным, и вот почему. Во-первых, классы
объектов домена, которые реализуют логику, специфичную для приложения (и зависят
от шлюзов и других объектов, специфичных для приложения), менее подходят для
повторного использования другими приложениями. Это должны быть модели частей
предметной области, представляющих интерес для данного приложения, поэтому подобные
объекты вовсе не обязаны описывать возможные отклики на все варианты использования
приложения. Во-вторых, инкапсуляция логики приложения на более высоком
уровне (каковым не является слой источника данных) облегчает изменение реализации
этого слоя, возможно, посредством некоторых специальных инструментальных средств.

Some might also argue that the application logic responsibilities could be implemented in domain object
methods, such as Contract.calculateRevenueRecognitions(), or even in the data source layer,
thereby eliminating the need for a separate Service Layer. However, I find those allocations of responsibility
undesirable for a number of reasons. First, domain object classes are less reusable across applications if they
implement application-specific logic (and depend on application-specific Gateways (466), and the like). They
should model the parts of the problem domain that are of interest to the application, which doesn’t mean all of
application’s use case responsibilities. Second, encapsulating application logic in a “higher” layer
dedicated to that purpose (which the data source layer isn’t) facilitates changing the implementation of that
layer perhaps to use a workflow engine.
(“Patterns of Enterprise Application Architecture” )

Я часто наблюдал такую проблему, когда в Django Model добавлялось какое-то новое поле, и начинали сыпаться проблемы, так как это имя уже было использовано либо с помощью аннотаций, либо с помощью Raw-SQL.
Также реализация аннотаций в Django ORM делает невозможным использование паттерна Identity Map.
Storm ORM/SQLAlchemy реализуют аннотации более удачно.
Если Вам все-таки пришлось работать с Django Model, воздержитесь от использования механизма Django аннотаций в пользу голого паттерна DataMapper.

Использование концепции агрегата и библиотек реактивного программирования, таких как RxJS, позволяет реализовывать Сервисный Слой с помощью простейшего паттерна Gateway, смотрите, например, учебный пример из документации Angular.
В таком случае, Query Object обычно реализуется в виде простого словаря, который преобразуется в список GET-параметров URL.
Общается такой Сервис с сервером обычно либо посредством JSON-RPC, либо посредством REST-API Actions.

Все работает хорошо до тех пор, пока не возникает необходимость выражать приоритезированные запросы, например, использующие логический оператор OR, который использует меньший приоритет чем логический оператор AND.
Это порождает вопрос, кто должен отвечать за построение запроса, Сервисный Слой клиента или Сервисный Слой сервера?

С одной стороны, сервер не должен делать предположений о своих клиентах, и должен ограничивать свой интерфейс посредством интерфейса Query Object.
Но это резко увеличивает уровень сложности клиента, в частности, при реализации Service Stub.
Для облегчения реализации можно использовать библиотеку rql, упомянутую в статье “Реализация паттерна Repository в браузерном JavaScript”.

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

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

Одна из самых острых проблем — это проблема параллельного обновления данных.

Все состояния гонки (race condition), взаимоблокировки (deadlocks) и проблемы параллельного обновления обусловлены изменяемостью переменных.
Если в программе нет изменяемых переменных, она никогда не окажется в состоянии гонки и никогда не столкнется с проблемами одновременного изменения.
В отсутствие изменяемых блокировок программа не может попасть в состояние взаимоблокировки.

All race conditions, deadlock conditions, and concurrent update problems are due to mutable variables.
You cannot have a race condition or a concurrent update problem if no variable is ever updated.
You cannot have deadlocks without mutable locks.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin

Любой порядок выражается в правильном наложении ограничений.

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

“it allows us to host the two services differently eg: we can host the read service on 25 servers and the write service on two.
The processing of commands and queries is fundamentally asymmetrical, and scaling the services symmetrically does not make a lot of sense.”

— “CQRS, Task Based UIs, Event Sourcing agh!” by Greg Young

Управление Логикой Приложения и Бизнес-Логикой хорошо раскрывается в статье “Clarified CQRS” by Udi Dahan.

Использование CQRS способствует использованию парадигмы Функционального Программирования.

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

— Нет, эти две парадигмы не являются конкурентами, они успешно могут дополнять друг друга. Тем не менее, тенденция к функциональному программированию является важной и интересной.

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

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

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

— Да, я кажется читал эту статью, которая затем вошла в качестве одной из глав в книгу “Beautiful Architecture”.

— Вы знаете об этом? Я очень впечатлен.

— (Смеюсь…) Да, и насколько я помню, это был ваш ответ на статью Саймона Пейтона Джонса, в которой автор старался показать, что ФП подход является более предпочтительным.

— Да, совершенно верно.

ПРИМЕЧАНИЕ: Речь идет о статье Бертрана “Software Architecture: Functional vs. Object-Oriented Design in Beautiful Architecture”, опубликованной в книге “Идеальная архитектура. Ведущие специалисты о красоте программных архитектур.”. Эта статья Мейера была ответом на статью Саймона “Composing contracts: an adventure in financial engineering.”

— Давайте все же немного вернемся к вопросу OOP vs FP. Какие именно преимущества у функционального подхода на “низком уровне”?

— В Eiffel существует очень важный принцип, под названием Command-Query Separation Principle, который можно рассматривать, в некотором роде, как сближение ОО и ФП миров. Я не считаю, что наличие состояния – это однозначно плохо. Но очень важно, чтобы мы могли ясно различать операции, которые это состояние изменяют (т.е. командами), и операции, которые лишь возвращают информацию о состоянии, его не изменяя (т.е. запросами). В других языках эта разница отсутствует. Так, например, в С/С++ часто пишут функции, которые возвращают результат и изменяют состояние. Следование этому принципу позволяет безопасно использовать выражения с запросами зная, что они не изменяют состояние. В некоторых случаях можно пойти еще дальше и работать в чисто функциональном мире с полным отсутствием побочных эффектов.

— Bertrand Meyer в интервью Сергея Теплякова “Интервью с Бертраном Мейером“

For both theoretical and practical reasons detailed elsewhere [10], the command-query separation principle is a methodological rule, not a language feature, but all serious software developed in Eiffel observes it scrupulously, to great referential transparency advantage.
Although other schools of object-oriented programming regrettable do not apply it (continuing instead the C style of calling functions rather than procedures to achieve changes), but in my view it is a key element of the object-oriented approach.
It seems like a viable way to obtain the referential transparency goal of functional programming — since expressions, which only involve queries, will not change the state, and hence can be understood as in traditional mathematics or a functional language — while acknowledging, through the notion of command, the fundamental role of the concept of state in modeling systems and computations.

— “Software architecture: object-oriented vs functional” by Bertrand Meyer

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

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

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

Решением этой проблемы обычно является замена CRUD (Create, Read, Update, Delete) на CR, т.е. наложение ограничения на изменение (Update) и удаление (Delete) записей в хранилище, что получило распространение под термином Event Sourcing.
Существуют специализированные хранилища, реализующие его, но он реализуется не обязательно специализированными инструментами.

Если CQRS позволяет работать с хранилищами данных в Императивном стиле, и отделяет действия (побочный эффект) от запроса (чтения) данных, то Event Sourcing идет еще дальше, и накладывает ограничение на изменение и удаление данных, превращая CRUD в CR.
Такой шаблон позволяет работать с хранилищами данных в Функциональном стиле, и предоставляет такие же выгоды: нет изменяемого состояния — нет проблемы параллельного обновления.
И такие же недостатки — потребность в большом количестве памяти и процессорной мощности.
Именно поэтому, данный шаблон широко используется в распределенных системах, где остро проявляется потребность в его достоинствах, и, вместе с тем, не проявляются его недостатки (ведь распределенные системы не лимитированы ни в памяти, ни в процессорной мощности).

Наглядным примером Event Sourcing может быть принцип организации банковского счета в базе данных, когда счет не является источником истины, а просто отражает совокупное значение всех транзакций (т.е. событий).

Наиболее ясно эта тема раскрывается в Chapter 6 “Functional Programming” of “Clean Architecture” by Robert C. Martin.

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

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

Если это все еще кажется вам абсурдным, вспомните, как работают системы управления версиями исходного кода.

More importantly, nothing ever gets deleted or updated from such a data store.
As a consequence, our applications are not CRUD; they are just CR. Also, because neither updates nor deletions occur in the data store, there cannot be any concurrent update issues.

If we have enough storage and enough processor power, we can make our applications entirely immutable—and, therefore, entirely functional.

If this still sounds absurd, it might help if you remembered that this is precisely the way your source code control system works.

— “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin

Event Sourcing is naturally functional.
It’s an append only log of facts that have happened in the past.
You can say that any projection any state is a left fold over your previous history.

— Greg Young, “A Decade of DDD, CQRS, Event Sourcing” at 16:44

I have always said that Event Sourcing is “Functional Data Storage”.
In this talk we will try migrating to a idiomatic functional way of looking at Event Sourcing.
Come and watch all the code disappear!
By the time you leave you will never want an “Event Sourcing Framework (TM)” ever again!

— Greg Young, “Functional Data”, NDC Conferences

  • “Clean Code: A Handbook of Agile Software Craftsmanship” by Robert C. Martin , chapters:
    • Dependency Injection … 157
    • Cross-Cutting Concerns … 160
    • Java Proxies … 161
    • Pure Java AOP Frameworks … 163
  • “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” by Robert C. Martin
    • Chapter 6 Functional Programming : Event Sourcing
    • Chapter 16 Independence
    • Chapter 18 Boundary Anatomy : Services
    • Chapter 20 Business Rules
    • Chapter 22 The Clean Architecture
    • Chapter 34 The Missing Chapter
  • “Patterns of Enterprise Application Architecture” by Martin Fowler , chapters:
    • Part 1. The Narratives : Chapter 2. Organizing Domain Logic : Service Layer
    • Part 1. The Narratives : Chapter 8. Putting It All Together
    • Part 2. The Patterns : Chapter 9. Domain Logic Patterns : Service Layer
  • “Domain-Driven Design: Tackling Complexity in the Heart of Software” by Eric Evans , chapters:
    • Part II: The Building Blocks of a Model-Driven Design : Chapter Four. Isolating the Domain : Layered Architecture
    • Part II: The Building Blocks of a Model-Driven Design : Chapter Five. A Model Expressed in Software : Services
  • “Implementing Domain-Driven Design” by Vaughn Vernon
    • Chapter 4 Architecture : Command-Query Responsibility Segregation, or CQRS
    • Chapter 4 Architecture : Event-Driven Architecture : Long-Running Processes, aka Sagas
    • Chapter 4 Architecture : Event-Driven Architecture : Event Sourcing
    • Chapter 7 Services
    • Chapter 14 Application : Application Services
    • Appendix A Aggregates and Event Sourcing: A+ES : Inside an Application Service
  • “Microsoft Application Architecture Guide” 2nd Edition (Patterns & Practices) by Microsoft Corporation (J.D. Meier, David Hill, Alex Homer, Jason Taylor, Prashant Bansode, Lonnie Wall, Rob Boucher Jr., Akshay Bogawat), chapters:
  • “Microsoft .NET: Architecting Applications for the Enterprise” 2nd Edition by Dino Esposito, Andrea Saltarello, chapters:
    • Chapter 5 Discovering the domain architecture : The layered architecture … 129
    • Chapter 10 Introducing CQRS … 255
    • Chapter 11 Implementing CQRS … 291
    • Chapter 12 Introducing event sourcing … 311
    • Chapter 13 Implementing event sourcing … 325
  • “Design Patterns Elements of Reusable Object-Oriented Software” by Erich Gamma , chapters:
    • Design Pattern Catalog : 4 Structural Patterns : Adapter … 139
    • Design Pattern Catalog : 4 Structural Patterns : Decorator … 175
  • “Building Microservices. Designing Fine-Grained Systems” by Sam Newman, chapters:
    • Chapter 3 How to Model Services : Premature Decomposition … 33
  • “Monolith to Microservices Evolutionary Patterns to Transform Your Monolith” by Sam Newman
    • Chapter 4. Decomposing the Database : Sagas
  • “Cloud Design Patterns. Prescriptive architecture guidance for cloud applications” by Alex Homer, John Sharp, Larry Brader, Masashi Narumoto, Trent Swanson, chapters:
  • “.NET Microservices: Architecture for Containerized .NET Applications” edition v2.2.1 (mirror) by Cesar de la Torre, Bill Wagner, Mike Rousos, chapters:
  • “CQRS Journey” by Dominic Betts, Julián Domínguez, Grigori Melnik, Fernando Simonazzi, Mani Subramanian, chapters:
  • “Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions” by Gregor Hohpe, Bobby Woolf, chapters:
      1. Message routing : Process manager … 278
  • “Microservices Patterns: With examples in Java” 1st Edition by Chris Richardson
  • “CQRS“

  • “Command Query Separation

  • “Event Sourcing“

  • “What do you mean by “Event-Driven”?“

  • “Patterns for Accounting“

  • “Accounting Patterns“

  • “CQRS, Task Based UIs, Event Sourcing agh!” by Greg Young

  • “Clarified CQRS” by Udi Dahan

  • “CQRS Documents” by Greg Young

  • “Sagas” by Hector Garcia-Molina and Kenneth Salem

  • “Domain services vs Application services” by Vladimir Khorikov

  • “Sagas” by Clemens Vasters (“Sample Code”)

This article in English “Design of Service Layer and Application Logic”.

Footnotes

Работа с метриками WAF‑ноды в Zabbix

Перейдите в браузере по адресу http://10.0.30.30, чтобы попасть на страницу логина веб‑интерфейса Zabbix. Войдите в веб‑интерфейс, используя стандартные логин (Admin) и пароль (zabbix).

Для мониторинга метрик WAF‑ноды node.example.local выполните следующие действия:

  1. Создайте новый хост. Для этого выполните следующие действия:

    1. Перейдите на вкладку Configuration → Hosts и нажмите не кнопку Create host.
    2. Введите полное доменное имя хоста в поле Host name (node.example.local).
    3. Выберите группу, куда требуется поместить создаваемый хост, в поле Groups (например, можно использовать предопределенную группу «Linux servers»).
    4. Введите IP‑адрес хоста с WAF‑нодой (10.0.30.5) в группе параметров Agent interfaces. Необходимо оставить значение порта по умолчанию (10050).

      Подключение с помощью доменного имени

      При необходимости вы можете настроить использование доменного имени хоста для подключения к Zabbix‑агенту. Для этого измените соответствующие параметры требуемым образом.

    5. Настройте прочие параметры, если это необходимо.

    6. Убедитесь, что поставлена галочка Enabled.
    7. Завершите процесс создания хоста, нажав на кнопку Add.
  2. Добавьте метрики, мониторинг которых необходимо осуществлять на хосте. Для добавления единичной метрики выполните следующие действия:

    1. Щелкните по имени созданного хоста node.example.local в списке хостов на вкладке Configuration → Hosts.
    2. На открывшейся странице с данными хоста перейдите на вкладку Items и нажмите на кнопку Create item.
    3. Введите имя метрики в поле Name (например, «Wallarm NGINX Attacks»).
    4. Значение параметров Type, Host interface и Type of information оставьте без изменения.
    5. Введите ключевое имя метрики (так, как оно указано в UserParameter= в конфигурации Zabbix‑агента, например, wallarm_nginx-gauge-attacks) в поле Key.
    6. При необходимости настройте частоту обновления значения метрики и другие требуемые параметры.
    7. Убедитесь, что поставлена галочка Enabled.
    8. Завершите процесс добавления метрики, нажав на кнопку Add.
  3. Настройте визуализацию добавленной метрики. Для этого:

    1. Нажмите на логотип Zabbix в левом верхнем углу веб‑интерфейса, чтобы перейти к дэшборду.
    2. Нажмите на кнопку Edit dashboard для того, чтобы внести изменения в дэшборд:

      1. Добавьте виджет, нажав на кнопку Add widget.
      2. Выберите требуемый тип виджета (например, «Plain Text») из выпадающего списка Type.
      3. Введите любое подходящее имя в поле Name.
      4. Добавьте необходимую метрику в список Items (созданную ранее «Wallarm NGINX Attacks»).
      5. Убедитесь, что поставлены галочки Show text as HTML и Dynamic Items.
      6. Завершите процесс добавления виджета, нажав на кнопку Add.
    3. Сохраните внесенные в дэшборд изменения, нажав на кнопку Save changes.

  4. Проверьте работу мониторинга:

    1. Убедитесь, что текущее количество атак в виджете Zabbix совпадает с выводом wallarm-status на WAF‑ноде:

      1. Используйте команду curl http://127.0.0.8/wallarm-status, если используются настройки WAF‑ноды по умолчанию.
      2. Если мониторинг настроен иначе, см. файл конфигурации /etc/nginx/conf.d/wallarm-status.conf.
        Вывод команды будет аналогичен выводу ниже:
        ```
        {"requests":0,"attacks":0,"blocked":0,"abnormal":0,"tnt_errors":0,"api_errors":0,"requests_lost":0,"segfaults":0,"memfaults":0,"softmemfaults":0,"time_detect":0,"db_id":46,"lom_id":4,"proton_instances": { "total":2,"success":2,"fallback":0,"failed":0 },"stalled_workers_count":0,"stalled_workers":[] }
        ```
        
    2. Выполните тестовую атаку на приложение, защищенное WAF‑нодой. Для этого можно выполнить команду curl с вредоносным запросом к приложению или выполнить этот запрос в браузере.

      Пример

      curl -I “http://node.example.local/?id='or+1=1--a-<script>prompt(1)</script>'”
      
    3. Убедитесь, что счетчик атак увеличился как в выводе wallarm-status, так и в виджете Zabbix:

      {"requests":64,"attacks":16,"blocked":0,"abnormal":64,"tnt_errors":0,"api_errors":0,"requests_lost":0,"segfaults":0,"memfaults":0,"softmemfaults":0,"time_detect":0,"db_id":46,"lom_id":4,"proton_instances": { "total":2,"success":2,"fallback":0,"failed":0 },"stalled_workers_count":0,"stalled_workers":[] }
      

Теперь в дэшборде Zabbix отображаются значения метрики curl_json-wallarm_nginx/gauge-attacks WAF‑ноды node.example.local.

51. Подключение логики приложения к виджетам

51. Подключение логики приложения к виджетам

51. Подключение логики приложения к виджетам

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

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

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

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

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

    • Существует гораздо более общий механизм, позволяющий
      ваше приложение реагирует на многие другие типы
      вводы: нажатие или отпускание любой клавиши клавиатуры или
      кнопка мыши; движение мыши внутрь, вокруг,
      или вне виджета; и многие другие мероприятия.Как и в случае с
      команда обработчиков, в этом механизме
      вы пишете процедуры-обработчики, которые будут вызываться
      всякий раз, когда происходят определенные типы событий. Этот
      Механизм обсуждается в Разделе 54, «События».

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


Python Программирование с графическим интерфейсом пользователя с помощью Tkinter — Real Python

Python имеет множество графических интерфейсов, но Tkinter — единственная среда, встроенная в стандартную библиотеку Python. У Tkinter есть несколько сильных сторон. Это кроссплатформенный , поэтому тот же код работает в Windows, macOS и Linux. Визуальные элементы визуализируются с использованием собственных элементов операционной системы, поэтому приложения, созданные с помощью Tkinter, выглядят так, как будто они принадлежат той платформе, на которой они запущены.

Хотя Tkinter де-факто считается фреймворком Python GUI, он не лишен критики. Одна из заметных критических замечаний заключается в том, что графические интерфейсы, созданные с помощью Tkinter, выглядят устаревшими. Если вам нужен блестящий современный интерфейс, возможно, Tkinter — не то, что вам нужно.

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

Освоив эти навыки, выполняя упражнения в конце каждого раздела, вы свяжете все вместе, создав два приложения. Первый — это преобразователь температуры, а второй — текстовый редактор. Пришло время погрузиться в дело и посмотреть, как создать приложение с помощью Tkinter!

Создание вашего первого приложения с графическим интерфейсом пользователя Python с помощью Tkinter

Основным элементом графического интерфейса пользователя Tkinter является окно . Windows — это контейнеры, в которых живут все остальные элементы графического интерфейса.Эти другие элементы графического интерфейса пользователя, такие как текстовые поля, метки и кнопки, известны как виджеты . Виджеты содержатся внутри окон.

Сначала создайте окно, содержащее единственный виджет. Запустите новый сеанс оболочки Python и продолжайте!

Примечание: Все примеры кода в этом руководстве были протестированы в Windows, macOS и Ubuntu Linux 18.04 с версиями Python 3.6, 3.7 и 3.8.

Если вы установили Python с помощью официальных установщиков, доступных для Windows и macOS от python.org, то у вас не должно возникнуть проблем с запуском примера кода. Вы можете смело пропустить оставшуюся часть этой заметки и продолжить обучение!

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

Python на macOS с Homebrew:

Дистрибутив Python для macOS, доступный на Homebrew, не поставляется в комплекте с зависимостью Tcl / Tk, необходимой для Tkinter.Вместо этого используется версия системы по умолчанию. Эта версия может быть устаревшей и препятствовать импорту модуля Tkinter. Чтобы избежать этой проблемы, используйте официальный установщик macOS.

Ubuntu Linux 16.04:

Последняя версия Python, доступная в репозитории Ubuntu Linux 16.04 Universe, — 3.5. Вы можете установить последнюю версию с помощью deadsnakes PPA. Вот команды для настройки PPA и загрузки последней версии Python с правильной версией Tcl / Tk:

  $ sudo add-apt-репозиторий ppa: deadsnakes / ppa
$ sudo apt-get update
$ sudo apt-get install python3.8 python3-tk
  

Первые две команды добавляют deadsnakes PPA в список репозиториев вашей системы, а последняя команда устанавливает Python 3.8 и модуль Python GUI Tkinter.

Ubuntu Linux 18.04:

Вы можете установить последнюю версию Python с правильной версией Tcl / Tk из репозитория Universe с помощью следующей команды:

  $ sudo apt-get install python3.8 python3-tk
  

Это устанавливает Python 3.8, а также модуль Python GUI Tkinter.

Другие варианты Linux:

Если вам не удается получить работающую установку Python для вашей разновидности Linux, вы можете собрать Python с правильной версией Tcl / Tk из исходного кода. Пошаговое руководство по этому процессу можно найти в Руководстве по установке и настройке Python 3.

Когда ваша оболочка Python открыта, первое, что вам нужно сделать, это импортировать модуль Python GUI Tkinter:

>>>

  >>> import tkinter as tk
  

Окно является экземпляром класса Tk Tkinter.Создайте новое окно и назначьте его переменной window :

Когда вы выполняете приведенный выше код, на вашем экране появляется новое окно. Как это выглядит, зависит от вашей операционной системы:

В оставшейся части этого руководства вы будете видеть скриншоты Windows.

Добавление виджета

Теперь, когда у вас есть окно, вы можете добавить виджет. Используйте класс tk.Label , чтобы добавить текст в окно. Создайте виджет Label с текстом «Hello, Tkinter» и назначьте его переменной с именем приветствие :

>>>

  >>> приветствие = т.к.Ярлык (текст = "Привет, Ткинтер")
  

Окно, которое вы создали ранее, не меняется. Вы только что создали виджет Label , но еще не добавили его в окно. Есть несколько способов добавить виджеты в окно. Прямо сейчас вы можете использовать метод .pack () виджета Label :

Окно теперь выглядит так:

Когда вы помещаете виджет .pack () в окно, Tkinter изменяет размер окна настолько маленьким, насколько это возможно, при этом все еще полностью охватывая виджет.Теперь выполните следующее:

Кажется, ничего не происходит, но обратите внимание, что в оболочке не появляется новое приглашение.

window.mainloop () сообщает Python, что нужно запустить цикл событий Tkinter . Этот метод отслеживает события, такие как нажатия кнопок или нажатия клавиш, и блокирует выполнение любого кода, который идет после него, до тех пор, пока окно, для которого он вызван, не будет закрыто. Закройте окно, которое вы создали, и вы увидите новое приглашение, отображаемое в оболочке.

Предупреждение: Когда вы работаете с Tkinter из Python REPL, обновления окон применяются при выполнении каждой строки. Это , а не , когда программа Tkinter выполняется из файла Python!

Если вы не включите window.mainloop () в конце программы в файле Python, приложение Tkinter никогда не запустится и ничего не будет отображаться.

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

Проверьте свое понимание

Разверните блоки кода ниже, чтобы проверить ваше понимание:

Напишите полный сценарий Python, который создает окно Tkinter с текстом «Python Rock!» .

Окно должно выглядеть так:

Попробуйте это упражнение прямо сейчас.

Вы можете развернуть блок кода ниже, чтобы увидеть решение:

Вот одно из возможных решений:

  импорт tkinter as tk

окно = tk.Tk ()
label = tk.Label (text = "Питон крут!")
label.pack ()

window.mainloop ()
  

Имейте в виду, что ваш код может выглядеть иначе.

Когда будете готовы, можете переходить к следующему разделу.

Управление компоновкой с помощью диспетчеров геометрии

До сих пор вы добавляли виджеты в окна и виджеты Frame , используя .pack () , но вы не узнали, что именно делает этот метод. Давайте проясним! Макет приложения в Tkinter контролируется менеджерами геометрии . Хотя .pack () является примером диспетчера геометрии, он не единственный. У Tkinter есть еще два:

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

.pack () Диспетчер геометрии

.pack () использует алгоритм упаковки для размещения виджетов в Frame или окне в указанном порядке. Для данного виджета алгоритм упаковки состоит из двух основных шагов:

  1. Вычислите прямоугольную область , называемую участком , которая является достаточно высокой (или широкой), чтобы удерживать виджет, и заполняет оставшуюся ширину (или высоту) в окне пустым пространством.
  2. Центрировать виджет на участке , если не указано другое местоположение.

.pack () — мощный инструмент, но его трудно визуализировать. Лучший способ почувствовать .pack () — это посмотреть на несколько примеров. Посмотрите, что происходит, когда вы помещаете .pack () три виджета Label в Frame :

  импорт tkinter as tk

окно = tk.Tk ()

frame1 = tk.Frame (мастер = окно, ширина = 100, высота = 100, bg = "красный")
frame1.пакет()

frame2 = tk.Frame (мастер = окно, ширина = 50, высота = 50, bg = "желтый")
frame2.pack ()

frame3 = tk.Frame (мастер = окно, ширина = 25, высота = 25, bg = "синий")
frame3.pack ()

window.mainloop ()
  

.pack () по умолчанию помещает каждый Frame под предыдущим в том порядке, в котором они назначены окну:

Каждый Frame помещается в самое верхнее доступное положение. Красная рамка Рамка размещается в верхней части окна. Затем желтый Frame помещается чуть ниже красного, а синий Frame чуть ниже желтого.

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

.pack () принимает некоторые аргументы ключевого слова для более точной настройки размещения виджета.Например, вы можете установить аргумент ключевого слова fill , чтобы указать, в каком направлении должны заполняться фреймы. Возможные варианты: tk.X для заполнения в горизонтальном направлении, tk.Y для заполнения по вертикали и tk.BOTH для заполнения в обоих направлениях. Вот как сложить три фрейма так, чтобы каждый заполнял все окно по горизонтали:

  импорт tkinter as tk

окно = tk.Tk ()

frame1 = tk.Frame (мастер = окно, высота = 100, bg = "красный")
frame1.упаковка (fill = tk.X)

frame2 = tk.Frame (master = window, height = 50, bg = "желтый")
frame2.pack (fill = tk.X)

frame3 = tk.Frame (master = window, height = 25, bg = "blue")
frame3.pack (fill = tk.X)

window.mainloop ()
  

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

Окно, создаваемое этим скриптом, выглядит так:

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

Обратите внимание, что виджеты Frame не расширяются в вертикальном направлении.

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

  • тк. ТОП
  • т.К. НИЖНЯЯ
  • тк. Левый
  • тк. ПРАВА

Если вы не установите сторона , тогда .pack () будет автоматически использовать tk.TOP и разместить новые виджеты в верхней части окна или в самой верхней части окна, которое не виджет уже не занят. Например, следующий сценарий размещает три фрейма бок о бок слева направо и расширяет каждый фрейм, чтобы заполнить окно по вертикали:

  импорт tkinter as tk

окно = тк.Тк ()

frame1 = tk.Frame (мастер = окно, ширина = 200, высота = 100, bg = "красный")
frame1.pack (fill = tk.Y, side = tk.LEFT)

frame2 = tk.Frame (мастер = окно, ширина = 100, bg = "желтый")
frame2.pack (fill = tk.Y, side = tk.LEFT)

frame3 = tk.Frame (master = window, width = 50, bg = "blue")
frame3.pack (fill = tk.Y, side = tk.LEFT)

window.mainloop ()
  

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

Итоговое окно выглядит так:

Так же, как при установке fill = tk.X , чтобы кадры реагировали при изменении размера окна по горизонтали, вы можете установить fill = tk.Y , чтобы кадры реагировали при изменении размера окна по вертикали:

Чтобы сделать макет по-настоящему адаптивным, вы можете установить начальный размер для ваших фреймов, используя атрибуты width и height . Затем установите для аргумента ключевого слова fill .pack () значение tk.BOTH и установите для аргумента ключевого слова expand значение True :

  импорт tkinter as tk

окно = тк.Тк ()

frame1 = tk.Frame (мастер = окно, ширина = 200, высота = 100, bg = "красный")
frame1.pack (fill = tk.BOTH, side = tk.LEFT, expand = True)

frame2 = tk.Frame (мастер = окно, ширина = 100, bg = "желтый")
frame2.pack (fill = tk.BOTH, side = tk.LEFT, expand = True)

frame3 = tk.Frame (master = window, width = 50, bg = "blue")
frame3.pack (fill = tk.BOTH, side = tk.LEFT, expand = True)

window.mainloop ()
  

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

Довольно круто!

The

.place () Диспетчер геометрии

Вы можете использовать .place () от до для управления точным местоположением , которое виджет должен занимать в окне, или Frame . Вы должны предоставить два аргумента ключевого слова, x и y , которые определяют x- и y-координаты для верхнего левого угла виджета.И x , и y измеряются в пикселях, а не в текстовых единицах.

Имейте в виду, что начало координат (где x и y оба равны 0 ) — это верхний левый угол Frame или окна. Итак, вы можете думать об аргументе y для .place () как о количестве пикселей от верха окна, а об аргументе x как о количестве пикселей от левого края окна.

Вот пример того, как .место () менеджер по геометрии работ:

  1импорт tkinter as tk
 2
 3window = tk.Tk ()
 4
 5frame = tk.Frame (мастер = окно, ширина = 150, высота = 150)
 6frame.pack ()
 7
 8label1 = tk.Label (master = frame, text = "Я на (0, 0)", bg = "red")
 9label1.place (x = 0, y = 0)
10
11label2 = tk.Label (master = frame, text = "Я на (75, 75)", bg = "yellow")
12label2.place (x = 75, y = 75)
13
14window.mainloop ()
  

Вот как работает этот код:

  • Строки 5 и 6 создают новый виджет Frame с именем frame1 , который имеет ширину 150 пикселей и высоту 150 пикселей, и упаковывает его в окно с помощью .пакет () .
  • Строки 8 и 9 создают новую метку Label с именем label1 с желтым фоном и помещают ее в frame1 в позиции (0, 0).
  • Строки 11 и 12 создают вторую метку Label с именем label2 с красным фоном и помещают ее в frame1 в позиции (75, 75).

Вот окно, которое выдает код:

.place () используется нечасто.У него два основных недостатка:

  1. Макетом может быть сложно управлять с помощью .place () . Это особенно верно, если в вашем приложении много виджетов.
  2. Макеты, созданные с помощью .place () , не отвечают. Они не меняются при изменении размера окна.

Одной из основных проблем разработки кроссплатформенного графического интерфейса является создание макетов, которые хорошо выглядят независимо от того, на какой платформе они просматриваются, а .place () — плохой выбор для создания адаптивных и кроссплатформенных макетов.

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

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

Сетка

() Диспетчер геометрии

Чаще всего вы будете использовать диспетчер геометрии .grid () , который предоставляет все возможности .pack () в формате, который легче понять и поддерживать.

.grid () работает, разделяя окно или Frame на строки и столбцы. Вы указываете местоположение виджета, вызывая .grid () и передавая индексы строки и столбца аргументам ключевого слова row и column соответственно. Индексы строки и столбца начинаются с 0 , поэтому индекс строки 1 и индекс столбца 2 сообщает .grid () разместить виджет в третьем столбце второй строки.

Следующий скрипт создает сетку кадров 3 × 3 с упакованными в них виджетами Label :

  импорт tkinter as tk

окно = tk.Tk ()

для i в диапазоне (3):
    для j в диапазоне (3):
        frame = tk.Frame (
            мастер = окно,
            рельеф = т.к. НАБОР,
            borderwidth = 1
        )
        frame.grid (строка = i, столбец = j)
        label = tk.Label (master = frame, text = f "Строка {i} \ nColumn {j}")
        label.pack ()

window.mainloop ()
  

Вот как выглядит результирующее окно:

В этом примере используются два менеджера геометрии.Каждый Frame прикреплен к окну с помощью менеджера геометрии .grid () :

  импорт tkinter as tk

окно = tk.Tk ()

для i в диапазоне (3):
    для j в диапазоне (3):
        frame = tk.Frame (
            мастер = окно,
            рельеф = т.к. НАБОР,
            borderwidth = 1
        )
        frame.grid (строка = i, столбец = j)
        label = tk.Label (master = frame, text = f "Строка {i} \ nColumn {j}")
        label.pack ()

window.mainloop ()
  

Каждая этикетка прикреплена к своей главной Frame с .pack () :

  импорт tkinter as tk

окно = tk.Tk ()

для i в диапазоне (3):
    для j в диапазоне (3):
        frame = tk.Frame (
            мастер = окно,
            рельеф = т.к. НАБОР,
            borderwidth = 1
        )
        frame.grid (строка = i, столбец = j)
        label = tk.Label (master = frame, text = f "Строка {i} \ nColumn {j}")
        label.pack ()

window.mainloop ()
  

Здесь важно понимать, что даже несмотря на то, что .grid () вызывается для каждого объекта Frame , диспетчер геометрии применяется к объекту window .Точно так же компоновка каждого кадра управляется с помощью диспетчера геометрии .pack () .

Рамки в предыдущем примере расположены вплотную друг к другу. Чтобы добавить пространство вокруг каждого кадра Frame , вы можете установить отступ для каждой ячейки в сетке. Padding — это просто пустое пространство, которое окружает виджет и визуально отделяет его от содержимого.

Два типа заполнения: внешнее, , и внутреннее заполнение , .Внешний отступ добавляет некоторое пространство за пределами ячейки сетки. Он управляется двумя ключевыми аргументами для .grid () :

.

  1. padx добавляет отступ в горизонтальном направлении.
  2. pady добавляет отступ в вертикальном направлении.

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

  импорт tkinter as tk

окно = tk.Tk ()

для i в диапазоне (3):
    для j в диапазоне (3):
        frame = tk.Frame (
            мастер = окно,
            рельеф = т.к. НАБОР,
            borderwidth = 1
        )
        frame.grid (строка = i, столбец = j, padx = 5, pady = 5)
        label = tk.Label (master = frame, text = f "Строка {i} \ nColumn {j}")
        label.pack ()

window.mainloop ()
  

Вот итоговое окно:

.pack () также имеет параметры padx и pady . Следующий код почти идентичен предыдущему, за исключением того, что вы добавляете 5 пикселей дополнительного отступа вокруг каждой Label как в направлениях x , так и y :

  импорт tkinter as tk

окно = tk.Tk ()

для i в диапазоне (3):
    для j в диапазоне (3):
        frame = tk.Frame (
            мастер = окно,
            рельеф = т.к. НАБОР,
            borderwidth = 1
        )
        Рамка.сетка (строка = i, столбец = j, padx = 5, pady = 5)

        label = tk.Label (master = frame, text = f "Строка {i} \ nColumn {j}")
        label.pack (padx = 5, pady = 5)

window.mainloop ()
  

Дополнительный отступ вокруг виджетов Label дает каждой ячейке в сетке немного свободного пространства между рамкой Frame и текстом в Label :

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

Вся сетка остается в верхнем левом углу при расширении окна.

Вы можете настроить рост строк и столбцов сетки при изменении размера окна с помощью .columnconfigure () и .rowconfigure () в объекте window . Помните, что сетка прикреплена к окну , даже если вы вызываете .grid () для каждого виджета Frame . И .columnconfigure () , и .rowconfigure () принимают три важных аргумента:

  1. Индекс столбца или строки сетки , который вы хотите настроить (или список индексов для одновременной настройки нескольких строк или столбцов)
  2. Аргумент ключевого слова с именем weight , который определяет, как столбец или строка должны реагировать на изменение размера окна относительно других столбцов и строк
  3. Аргумент ключевого слова с именем minsize , который устанавливает минимальный размер высоты строки или ширины столбца в пикселях

Вес по умолчанию равен 0 , что означает, что столбец или строка не расширяются при изменении размера окна.Если каждому столбцу и строке присвоить вес 1 , то все они будут расти с одинаковой скоростью. Если один столбец имеет вес 1 , а другой — 2 , то второй столбец расширяется в два раза быстрее, чем первый. Измените предыдущий сценарий, чтобы лучше обрабатывать изменение размера окна:

  импорт tkinter as tk

окно = tk.Tk ()

для i в диапазоне (3):
    window.columnconfigure (я, вес = 1, минимальный размер = 75)
    window.rowconfigure (я, вес = 1, минимальный размер = 50)

    для j в диапазоне (0, 3):
        рамка = тк.Рамка(
            мастер = окно,
            рельеф = т.к. НАБОР,
            borderwidth = 1
        )
        frame.grid (строка = i, столбец = j, padx = 5, pady = 5)

        label = tk.Label (master = frame, text = f "Строка {i} \ nColumn {j}")
        label.pack (padx = 5, pady = 5)

window.mainloop ()
  

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

На каждой итерации цикла столбец и строка i конфигурируются так, чтобы иметь вес , равный 1 . Это гарантирует, что каждая строка и столбец расширяются с одинаковой скоростью при изменении размера окна. Аргумент minsize устанавливается равным 75 для каждого столбца и 50 для каждой строки. Это гарантирует, что виджет Label всегда отображает свой текст без обрезания каких-либо символов, даже если размер окна очень мал.

В результате получается сетка, которая плавно расширяется и сжимается при изменении размера окна:

Попробуйте сами, чтобы понять, как это работает! Поиграйте с параметрами weight и minsize , чтобы увидеть, как они влияют на сетку.

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

  импорт tkinter as tk

окно = тк.Тк ()
window.columnconfigure (0, минимальный размер = 250)
window.rowconfigure ([0, 1], minsize = 100)

label1 = tk.Label (text = "A")
label1.grid (строка = 0, столбец = 0)

label2 = tk.Label (текст = "B")
label2.grid (строка = 1, столбец = 0)

window.mainloop ()
  

Каждая ячейка сетки имеет ширину 250 пикселей и высоту 100 пикселей. Метки помещаются в центре каждой ячейки, как вы можете видеть на следующем рисунке:

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

  • "n" или "N" для выравнивания по верхней центральной части ячейки
  • "e" или "E" для выравнивания по правой центральной стороне ячейки
  • "s" или "S" для выравнивания по нижней центральной части ячейки
  • "w" или "W" для выравнивания по левой центральной стороне ячейки

Буквы «n» , «s» , «e» и «w» исходят от сторон света, севера, юга, востока и запада.Установка липкого на "n" на обеих этикетках в предыдущих позициях кода каждая Этикетка в верхнем центре своей ячейки сетки:

  импорт tkinter as tk

окно = tk.Tk ()
window.columnconfigure (0, минимальный размер = 250)
window.rowconfigure ([0, 1], minsize = 100)

label1 = tk.Label (text = "A")
label1.grid (row = 0, column = 0, sticky = "n")

label2 = tk.Label (текст = "B")
label2.grid (row = 1, column = 0, sticky = "n")

window.mainloop ()
  

Вот результат:

Вы можете объединить несколько букв в одну строку, чтобы разместить каждую Label в углу своей ячейки сетки:

  импорт tkinter as tk

окно = тк.Тк ()
window.columnconfigure (0, минимальный размер = 250)
window.rowconfigure ([0, 1], minsize = 100)

label1 = tk.Label (text = "A")
label1.grid (row = 0, column = 0, sticky = "ne")

label2 = tk.Label (текст = "B")
label2.grid (строка = 1, столбец = 0, sticky = "sw")

window.mainloop ()
  

В этом примере параметр sticky для label1 установлен на "ne" , что помещает метку в правый верхний угол ячейки сетки. label2 позиционируется в нижнем левом углу путем передачи "sw" на sticky .Вот как это выглядит в окне:

Когда виджет позиционируется с помощью sticky , размер самого виджета достаточно велик, чтобы содержать внутри него любой текст и другое содержимое. Он не заполнит всю ячейку сетки. Чтобы заполнить сетку, вы можете указать «нс», , чтобы виджет заполнял ячейку в вертикальном направлении, или «ew», , чтобы заполнить ячейку в горизонтальном направлении. Чтобы заполнить всю ячейку, установите sticky на "nsew" .Следующий пример иллюстрирует каждый из этих вариантов:

  импорт tkinter as tk

окно = tk.Tk ()

window.rowconfigure (0, минимальный размер = 50)
window.columnconfigure ([0, 1, 2, 3], minsize = 50)

label1 = tk.Label (text = "1", bg = "black", fg = "white")
label2 = tk.Label (text = "2", bg = "black", fg = "white")
label3 = tk.Label (text = "3", bg = "black", fg = "white")
label4 = tk.Label (text = "4", bg = "black", fg = "white")

label1.grid (строка = 0, столбец = 0)
label2.grid (row = 0, column = 1, sticky = "ew")
label3.grid (row = 0, column = 2, sticky = "ns")
label4.сетка (строка = 0, столбец = 3, липкий = "nsew")

window.mainloop ()
  

Вот как выглядит результат:

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

.сетка () . Упаковка ()
sticky = "ns" fill = tk.Y
sticky = "ew" fill = tk.X
sticky = "nsew" fill = tk.BOTH

.grid () — мощный менеджер геометрии. Часто его легче понять, чем .pack () , и он намного более гибкий, чем .место () . Когда вы создаете новые приложения Tkinter, вам следует рассмотреть возможность использования .grid () в качестве основного диспетчера геометрии.

Примечание. .grid () предлагает гораздо большую гибкость, чем вы видели здесь. Например, вы можете настроить ячейки так, чтобы они занимали несколько строк и столбцов. Для получения дополнительной информации ознакомьтесь с разделом Grid Geometry Manager учебника TkDocs.

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

Проверьте свое понимание

Разверните блок кода ниже, чтобы проверить свое понимание:

Ниже приведено изображение формы ввода адреса, созданной с помощью Tkinter.

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

Вы можете развернуть блок кода ниже, чтобы увидеть решение:

Есть много разных способов решить это упражнение. Если ваше решение генерирует окно, идентичное окну в инструкции упражнения, поздравляем! Вы успешно выполнили упражнение! Ниже вы можете посмотреть на два решения, в которых используется .grid () менеджер геометрии.

Одно решение создает виджет Label и Entry для каждого поля с желаемыми настройками:

  импорт tkinter as tk

# Создайте новое окно с заголовком «Форма ввода адреса»
окно = tk.Tk ()
window.title ("Форма ввода адреса")

# Создаем новый фрейм `frm_form`, содержащий метку
Виджеты # и Entry для ввода адресной информации.
frm_form = tk.Frame (рельеф = tk.SUNKEN, borderwidth = 3)
# Упакуем рамку в окно
frm_form.пакет()

# Создайте виджеты Label и Entry для "First Name"
lbl_first_name = tk.Label (master = frm_form, text = "Имя:")
ent_first_name = tk.Entry (master = frm_form, width = 50)
# Используйте диспетчер геометрии сетки, чтобы разместить метку и
# Виджеты входа в первый и второй столбцы
# первая строка сетки
lbl_first_name.grid (строка = 0, столбец = 0, липкий = "e")
ent_first_name.grid (строка = 0, столбец = 1)

# Создайте виджеты "Ярлык" и "Ввод" для "Фамилии"
lbl_last_name = tk.Label (master = frm_form, text = "Фамилия:")
ent_last_name = tk.Запись (master = frm_form, width = 50)
# Поместите виджеты во вторую строку сетки
lbl_last_name.grid (строка = 1, столбец = 0, липкий = "e")
ent_last_name.grid (строка = 1, столбец = 1)

# Создайте виджеты Label и Entry для "Address Line 1"
lbl_address1 = tk.Label (master = frm_form, text = "Адресная строка 1:")
ent_address1 = tk.Entry (master = frm_form, width = 50)
# Поместите виджеты в третью строку сетки
lbl_address1.grid (строка = 2, столбец = 0, липкий = "e")
ent_address1.grid (строка = 2, столбец = 1)

# Создайте виджеты Label и Entry для "Address Line 2"
lbl_address2 = tk.Ярлык (master = frm_form, text = "Адресная строка 2:")
ent_address2 = tk.Entry (master = frm_form, width = 5)
# Поместите виджеты в четвертый ряд сетки
lbl_address2.grid (строка = 3, столбец = 0, липкий = tk.E)
ent_address2.grid (строка = 3, столбец = 1)

# Создайте виджеты Label и Entry для "City"
lbl_city = tk.Label (master = frm_form, text = "Город:")
ent_city = tk.Entry (master = frm_form, width = 50)
# Поместите виджеты в пятую строку сетки
lbl_city.grid (строка = 4, столбец = 0, липкий = tk.E)
ent_city.grid (строка = 4, столбец = 1)

# Создайте виджеты «Ярлык» и «Ввод» для «Штат / провинция»
lbl_state = тк.Ярлык (master = frm_form, text = "Штат / провинция:")
ent_state = tk.Entry (master = frm_form, ширина = 50)
# Размещаем виджеты в шестой строке сетки
lbl_state.grid (строка = 5, столбец = 0, липкий = tk.E)
ent_state.grid (строка = 5, столбец = 1)

# Создайте виджеты «Ярлык» и «Ввод» для «Почтового индекса»
lbl_postal_code = tk.Label (master = frm_form, text = "Почтовый индекс:")
ent_postal_code = tk.Entry (master = frm_form, ширина = 50)
# Поместите виджеты в седьмую строку сетки
lbl_postal_code.grid (строка = 6, столбец = 0, липкий = tk.E)
ent_postal_code.сетка (строка = 6, столбец = 1)

# Создайте виджеты Label и Entry для "Country"
lbl_country = tk.Label (master = frm_form, text = "Country:")
ent_country = tk.Entry (master = frm_form, width = 50)
# Поместите виджеты в восьмой ряд сетки
lbl_country.grid (строка = 7, столбец = 0, липкий = tk.E)
ent_country.grid (строка = 7, столбец = 1)

# Создаем новый фрейм `frm_buttons`, содержащий
# Кнопки «Отправить» и «Очистить». Этот кадр заполняет
# все окно в горизонтальном направлении и имеет
# 5 пикселей горизонтального и вертикального отступа.frm_buttons = tk.Frame ()
frm_buttons.pack (fill = tk.X, ipadx = 5, ipady = 5)

# Создайте кнопку «Отправить» и упакуйте ее в
# правая часть `frm_buttons`
btn_submit = tk.Button (master = frm_buttons, text = "Отправить")
btn_submit.pack (сторона = tk.RIGHT, padx = 10, ipadx = 10)

# Создайте кнопку "Очистить" и упакуйте ее в
# правая часть `frm_buttons`
btn_clear = tk.Button (master = frm_buttons, text = "Очистить")
btn_clear.pack (сторона = tk.RIGHT, ipadx = 10)

# Запустить приложение
window.mainloop ()
  

В этом решении нет ничего плохого.Это немного длинновато, но все очень ясно. Если вы хотите что-то изменить, тогда ясно, где именно это сделать.

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

  импорт tkinter as tk

# Создайте новое окно с заголовком «Форма ввода адреса»
окно = tk.Tk ()
window.title ("Форма ввода адреса")

# Создаем новый фрейм `frm_form`, содержащий метку
Виджеты # и Entry для ввода адресной информации.frm_form = tk.Frame (рельеф = tk.SUNKEN, borderwidth = 3)
# Упакуем рамку в окно
frm_form.pack ()

# Список меток полей
метки = [
    "Имя:",
    "Фамилия:",
    "Адресная строка 1:",
    "Адресная строка 2:",
    "Город:",
    "Штат / провинция:",
    "Почтовый индекс:",
    "Страна:",
]

# Перебрать список меток полей
для idx текст в перечислении (метки):
    # Создайте виджет метки с текстом из списка меток
    label = tk.Label (master = frm_form, text = текст)
    # Создать виджет Entry
    запись = tk.Запись (master = frm_form, width = 50)
    # Используйте диспетчер геометрии сетки, чтобы разместить метку и
    # Виджеты входа в строку с индексом idx
    label.grid (row = idx, column = 0, sticky = "e")
    entry.grid (строка = idx, столбец = 1)

# Создаем новый фрейм `frm_buttons`, содержащий
# Кнопки «Отправить» и «Очистить». Этот кадр заполняет
# все окно в горизонтальном направлении и имеет
# 5 пикселей горизонтального и вертикального отступа.
frm_buttons = tk.Frame ()
frm_buttons.pack (fill = tk.X, ipadx = 5, ipady = 5)

# Создайте кнопку «Отправить» и упакуйте ее в
# правая часть `frm_buttons`
btn_submit = тк.Кнопка (master = frm_buttons, text = "Отправить")
btn_submit.pack (сторона = tk.RIGHT, padx = 10, ipadx = 10)

# Создайте кнопку "Очистить" и упакуйте ее в
# правая часть `frm_buttons`
btn_clear = tk.Button (master = frm_buttons, text = "Очистить")
btn_clear.pack (сторона = tk.RIGHT, ipadx = 10)

# Запустить приложение
window.mainloop ()
  

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

Когда будете готовы, можете переходить к следующему разделу.

Создание интерактивных приложений

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

Использование событий и обработчиков событий

При создании приложения Tkinter необходимо вызвать окно .mainloop () для запуска цикла обработки событий . Во время цикла событий ваше приложение проверяет, произошло ли событие. Если да, то в ответ может быть выполнен какой-то код.

Цикл событий предоставляется в Tkinter, поэтому вам не нужно писать код, который проверяет события самостоятельно. Однако вам нужно написать код, который будет выполняться в ответ на событие. В Tkinter вы пишете функции, называемые обработчиками событий для событий, которые вы используете в своем приложении.

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

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

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

Предположим, есть список events_list , который содержит объекты событий. Новый объект события автоматически добавляется к events_list каждый раз, когда событие происходит в вашей программе. (Вам не нужно реализовывать этот механизм обновления. В этом концептуальном примере это происходит автоматически.) Используя бесконечный цикл, вы можете постоянно проверять, есть ли какие-либо объекты событий в events_list :

  # Предположим, что этот список обновляется автоматически
events_list = []

# Запускаем цикл событий
в то время как True:
    # Если список_событий пуст, значит, событий не произошло и вы
    # можно перейти к следующей итерации цикла
    если events_list == []:
        Продолжать

    # Если выполнение доходит до этой точки, то есть хотя бы один
    # объект события в events_list
    event = events_list [0]
  

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

Предположим, что событие имеет атрибут .type , установленный на строку "keypress" , если событие является объектом события нажатия клавиши, и атрибут .char , содержащий символ нажатой клавиши.Создайте новую функцию handle_keypress () и обновите код цикла событий:

  events_list = []

# Создать обработчик событий
def handle_keypress (событие):
    "" "Вывести символ, связанный с нажатой клавишей" ""
    печать (event.char)

в то время как True:
    если events_list == []:
        Продолжать
    event = events_list [0]

    # Если событие является объектом события нажатия клавиши
    если event.type == "нажатие клавиши":
        # Вызвать обработчик события нажатия клавиши
        handle_keypress (событие)
  

При звонке в окно .mainloop () , за вас запускается что-то вроде вышеуказанного цикла. Этот метод позаботится о двух частях цикла:

  1. Он поддерживает список событий , которые произошли.
  2. Он запускает обработчик событий каждый раз, когда новое событие добавляется в этот список.

Обновите цикл событий, чтобы использовать window.mainloop () вместо собственного цикла событий:

  импорт tkinter as tk

# Создать объект окна
окно = tk.Tk ()

# Создать обработчик событий
def handle_keypress (событие):
    "" "Вывести символ, связанный с нажатой клавишей" ""
    печать (событие.символ)

# Запускаем цикл событий
window.mainloop ()
  

.mainloop () позаботится обо всем за вас, но в приведенном выше коде чего-то не хватает. Как Tkinter узнает, когда использовать handle_keypress () ? Для этого в виджетах Tkinter есть метод .bind () .

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

.bind ()

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

  импорт tkinter as tk

окно = tk.Tk ()

def handle_keypress (событие):
    "" "Вывести символ, связанный с нажатой клавишей" ""
    печать (event.char)

# Привязать событие нажатия клавиши к handle_keypress ()
window.bind ("<Ключ>", handle_keypress)

window.mainloop ()
  

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

Примечание: Результатом вышеупомянутой программы является , а не , напечатанный в окне приложения Tkinter. Он печатается по адресу stdout .

Если вы запустите программу в IDLE, вы увидите результат в интерактивном окне. Если вы запустите программу с терминала, вы должны увидеть результат в своем терминале.

.bind () всегда принимает как минимум два аргумента:

  1. Событие , которое представлено строкой вида «» , где event_name может быть любым из событий Tkinter
  2. Обработчик событий — имя функции, вызываемой при возникновении события

Обработчик событий привязан к виджету, для которого вызывается .bind () . Когда вызывается обработчик событий, объект события передается функции обработчика событий.

В приведенном выше примере обработчик событий привязан к самому окну, но вы можете привязать обработчик событий к любому виджету в вашем приложении. Например, вы можете привязать обработчик событий к виджету Button , который будет выполнять некоторые действия при каждом нажатии кнопки:

  def handle_click (событие):
    print («Была нажата кнопка!»)

button = tk.Button (text = "Нажми меня!")

button.bind ("", handle_click)
  

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

Вы можете привязать любой обработчик событий к любому виду виджета с помощью .bind () , но есть более простой способ привязать обработчики событий к нажатию кнопки с помощью атрибута command виджета Button .

Использование команды

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

Взгляните на пример. Сначала вы создадите окно с виджетом Label , содержащим числовое значение. Вы разместите кнопки слева и справа от ярлыка. Левая кнопка будет использоваться для уменьшения значения в Label , а правая — для увеличения значения.Вот код окна:

  импорт tkinter as tk

окно = tk.Tk ()

window.rowconfigure (0, минимальный размер = 50, вес = 1)
window.columnconfigure ([0, 1, 2], minsize = 50, weight = 1)

btn_decrease = tk.Button (master = window, text = "-")
btn_decrease.grid (строка = 0, столбец = 0, sticky = "nsew")

lbl_value = tk.Label (master = window, text = "0")
lbl_value.grid (строка = 0, столбец = 1)

btn_increase = tk.Button (master = window, text = "+")
btn_increase.grid (строка = 0, столбец = 2, sticky = "nsew")

window.mainloop ()
  

Окно выглядит так:

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

  1. Как получить текст на этикетке ?
  2. Как обновить текст на этикетке ?

Виджеты Label не имеют .get () , в отличие от виджетов Entry и Text . Однако вы можете получить текст из метки, обратившись к атрибуту text с нотацией индекса в стиле словаря:

  этикетка = Tk.Ярлык (text = "Hello")

# Получить текст ярлыка
текст = метка ["текст"]

# Установить новый текст для метки
label ["text"] = "До свидания"
  

Теперь, когда вы знаете, как получить и установить текст метки, напишите функцию Увеличить () , которая увеличивает значение в lbl_value на 1:

.

  увеличение def ():
    значение = int (lbl_value ["текст"])
    lbl_value ["текст"] = f "{значение + 1}"
  

Increase () получает текст из lbl_value и преобразует его в целое число с помощью int () .Затем он увеличивает это значение на 1 и устанавливает для атрибута text метки это новое значение.

Вам также понадобится reduce () , чтобы уменьшить значение в value_label на 1:

.

  def уменьшение ():
    значение = int (lbl_value ["текст"])
    lbl_value ["текст"] = f "{значение - 1}"
  

Поместите увеличение () и уменьшение () в свой код сразу после оператора import .

Чтобы связать кнопки с функциями, назначьте функцию атрибуту команды кнопки.Вы можете сделать это при создании экземпляра кнопки. Например, чтобы назначить Увеличить () до Увеличить_кнопку , обновите строку, которая создает экземпляр кнопки, следующим образом:

  btn_increase = tk.Button (мастер = окно, текст = "+", команда = увеличение)
  

Теперь назначьте уменьшение () с на уменьшение_button :

  btn_decrease = tk.Button (master = window, text = "-", command = уменьшение)
  

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

Вот полный код приложения для справки:

  импорт tkinter as tk

def увеличить ():
    значение = int (lbl_value ["текст"])
    lbl_value ["текст"] = f "{значение + 1}"


def уменьшение ():
    значение = int (lbl_value ["текст"])
    lbl_value ["текст"] = f "{значение - 1}"

окно = tk.Tk ()

window.rowconfigure (0, минимальный размер = 50, вес = 1)
окно.columnconfigure ([0, 1, 2], minsize = 50, weight = 1)

btn_decrease = tk.Button (master = window, text = "-", command = уменьшение)
btn_decrease.grid (строка = 0, столбец = 0, sticky = "nsew")

lbl_value = tk.Label (master = window, text = "0")
lbl_value.grid (строка = 0, столбец = 1)

btn_increase = tk.Button (master = window, text = "+", command = увеличить)
btn_increase.grid (строка = 0, столбец = 2, sticky = "nsew")

window.mainloop ()
  

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

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

В следующих двух разделах вы создадите приложения, которые будут делать что-то полезное. Сначала вы создадите преобразователь температуры, который преобразует значение температуры из Фаренгейта в Цельсия. После этого вы создадите текстовый редактор, который сможет открывать, редактировать и сохранять текстовые файлы!

Проверьте свое понимание

Разверните блок кода ниже, чтобы проверить свое понимание:

Напишите программу, имитирующую прокатку шестигранной матрицы.Должна быть одна кнопка с текстом «Roll» . Когда пользователь нажимает кнопку, должно отображаться случайное целое число от 1 до 6 .

Окно приложения должно выглядеть примерно так:

Попробуйте это упражнение прямо сейчас.

Вы можете развернуть блок кода ниже, чтобы увидеть решение:

Вот одно из возможных решений:

  импорт случайный
импортировать tkinter как tk

def roll ():
    lbl_result ["текст"] = str (случайный.рандинт (1, 6))

окно = tk.Tk ()
window.columnconfigure (0, минимальный размер = 150)
window.rowconfigure ([0, 1], minsize = 50)

btn_roll = tk.Button (text = "Roll", command = roll)
lbl_result = tk.Label ()

btn_roll.grid (строка = 0, столбец = 0, sticky = "nsew")
lbl_result.grid (строка = 1, столбец = 0)

window.mainloop ()
  

Имейте в виду, что ваш код может выглядеть иначе.

Когда будете готовы, можете переходить к следующему разделу.

Создание преобразователя температуры (пример приложения)

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

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

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

  1. Виджет Entry вызвал ent_temperature для ввода значения по Фаренгейту
  2. Виджет Label вызвал lbl_result для отображения результата по Цельсию
  3. A Button виджет , вызываемый btn_convert , который считывает значение из виджета Entry , конвертирует его из Фаренгейта в градусы Цельсия и устанавливает текст виджета Label на результат при нажатии

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

Вы поместите метку прямо справа от виджета ent_temperature , содержащую символ Фаренгейта (℉), чтобы пользователь знал, что значение ent_temperature должно быть в градусах Фаренгейта. Для этого установите для текста метки значение "\ N {DEGREE FAHRENHEIT}" , в котором для отображения символа Фаренгейта используется поддержка именованных символов Unicode Python.

Вы можете придать btn_convert немного чутья, установив для его текста значение "\ N {RIGHTWARDS BLACK ARROW}" , которое отображает черную стрелку, указывающую вправо. Вы также убедитесь, что lbl_result всегда имеет символ Цельсия (℃) после текста метки "\ N {DEGREE CELSIUS}" , чтобы указать, что результат выражается в градусах Цельсия. Вот как будет выглядеть окончательное окно:

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

  импорт tkinter as tk

окно = тк.Тк ()
window.title («Конвертер температуры»)
  

window.title () устанавливает заголовок существующего окна. Когда вы, наконец, запустите это приложение, в строке заголовка окна появится текст Temperature Converter . Затем создайте виджет ent_tempera с меткой lbl_temp и назначьте оба виджету Frame с именем frm_entry :

  frm_entry = tk.Frame (мастер = окно)
ent_temperature = tk.Запись (master = frm_entry, width = 10)
lbl_temp = tk.Label (master = frm_entry, text = "\ N {DEGREE FAHRENHEIT}")
  

ent_temperature — это то место, где пользователь вводит значение по Фаренгейту. lbl_temp используется для обозначения ent_temperature символом Фаренгейта. frm_entry — это контейнер, который группирует вместе ent_temperature и lbl_temp .

Необходимо разместить lbl_temp непосредственно справа от ent_temp.Вы можете разместить их в frm_entry с помощью менеджера геометрии .grid () с одной строкой и двумя столбцами:

  ent_temperature.grid (строка = 0, столбец = 0, липкий = "e")
lbl_temp.grid (строка = 0, столбец = 1, липкий = "w")
  

Вы установили для параметра sticky значение "e" для ent_temperature , чтобы он всегда прилипал к самому правому краю своей ячейки сетки. Вы также устанавливаете sticky на "w" для lbl_temp , чтобы он оставался прикрепленным к самому левому краю своей ячейки сетки.Это гарантирует, что lbl_temp всегда будет располагаться сразу справа от ent_tempera .

Теперь сделайте btn_convert и lbl_result для преобразования введенной температуры в ent_temperature и отображения результатов:

  btn_convert = tk.Button (
    мастер = окно,
    text = "\ N {ЧЕРНАЯ СТРЕЛКА ВПРАВО}"
)
lbl_result = tk.Label (master = window, text = "\ N {DEGREE CELSIUS}")
  

Как и frm_entry , btn_convert и lbl_result назначаются окну .Вместе эти три виджета составляют три ячейки в основной сетке приложения. Используйте .grid () , чтобы разложить их сейчас:

  frm_entry.grid (строка = 0, столбец = 0, padx = 10)
btn_convert.grid (строка = 0, столбец = 1, pady = 10)
lbl_result.grid (строка = 0, столбец = 2, padx = 10)
  

Наконец, запустите приложение:

Отлично выглядит! Но кнопка пока ничего не делает. Вверху файла сценария, чуть ниже строки import , добавьте функцию с именем fahrenheit_to_celsius () :

.

  def fahrenheit_to_celsius ():
    "" "Преобразуйте значение Фаренгейта в Цельсий и вставьте
    результат в lbl_result."" "
    fahrenheit = ent_tempera.get ()
    по Цельсию = (5/9) * (с плавающей запятой (по Фаренгейту) - 32)
    lbl_result ["text"] = f "{round (celsius, 2)} \ N {DEGREE CELSIUS}"
  

Эта функция считывает значение из ent_temperature , преобразует его из Фаренгейта в Цельсия, а затем отображает результат в lbl_result .

Теперь перейдите к строке, где вы определяете btn_convert и задаете его параметр command равным fahrenheit_to_celsius :

  btn_convert = tk.Кнопка(
    мастер = окно,
    text = "\ N {ЧЕРНАЯ СТРЕЛКА ВПРАВО}",
    command = fahrenheit_to_celsius # <--- Добавить эту строку
)
  

Вот и все! Вы создали полнофункциональное приложение-преобразователь температуры всего в 26 строках кода! Довольно круто, правда?

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

Вот полный сценарий для справки:

  импорт tkinter as tk

def fahrenheit_to_celsius ():
    "" "Преобразуйте значение Фаренгейта в Цельсий и вставьте
    результат в lbl_result."" "
    fahrenheit = ent_tempera.get ()
    по Цельсию = (5/9) * (с плавающей запятой (по Фаренгейту) - 32)
    lbl_result ["text"] = f "{round (celsius, 2)} \ N {DEGREE CELSIUS}"

# Настроить окно
окно = tk.Tk ()
window.title («Конвертер температуры»)
window.resizable (ширина = ложь, высота = ложь)

# Создаем рамку ввода по Фаренгейту с Entry
# виджет и метка в нем
frm_entry = tk.Frame (мастер = окно)
ent_tempera = tk.Entry (master = frm_entry, width = 10)
lbl_temp = tk.Label (master = frm_entry, text = "\ N {DEGREE FAHRENHEIT}")

# Разместите запись температуры и метку в frm_entry
# с помощью .grid () менеджер геометрии
ent_tempera.grid (строка = 0, столбец = 0, липкий = "e")
lbl_temp.grid (строка = 0, столбец = 1, липкий = "w")

# Создаем кнопку преобразования и метку отображения результатов
btn_convert = tk.Button (
    мастер = окно,
    text = "\ N {ЧЕРНАЯ СТРЕЛКА ВПРАВО}",
    команда = fahrenheit_to_celsius
)
lbl_result = tk.Label (master = window, text = "\ N {DEGREE CELSIUS}")

# Настройте компоновку с помощью менеджера геометрии .grid ()
frm_entry.grid (строка = 0, столбец = 0, padx = 10)
btn_convert.grid (строка = 0, столбец = 1, pady = 10)
lbl_result.сетка (строка = 0, столбец = 2, padx = 10)

# Запускаем приложение
window.mainloop ()
  

Пришло время поднять ситуацию на ступеньку выше! Прочтите, чтобы узнать, как создать текстовый редактор.

Создание текстового редактора (пример приложения)

В этом разделе вы создадите приложение для редактирования текста , которое может создавать, открывать, редактировать и сохранять текстовые файлы. В заявке есть три основных элемента:

  1. Виджет Button , вызываемый btn_open для открытия файла для редактирования
  2. A Кнопка виджет вызывается btn_save для сохранения файла
  3. Виджет TextBox с именем txt_edit для создания и редактирования текстового файла

Три виджета будут расположены так, чтобы две кнопки находились с левой стороны окна, а текстовое поле - с правой стороны.Все окно должно иметь минимальную высоту 800 пикселей, а txt_edit должно иметь минимальную ширину 800 пикселей. Весь макет должен быть отзывчивым, чтобы при изменении размера окна изменился и размер txt_edit . Однако ширина рамки Frame , удерживающей кнопки, не должна изменяться.

Вот набросок того, как будет выглядеть окно:

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

  1. Узкая колонка слева для кнопок
  2. Более широкий столбец справа для текстового поля

Чтобы установить минимальные размеры для окна и txt_edit , вы можете установить параметры minsize методов окна .rowconfigure () и .columnconfigure () от до 800. Для изменения размера вы можете установить для параметра weight этих методов значение 1.

Чтобы поместить обе кнопки в один столбец, вам нужно создать виджет Frame с именем fr_buttons . Согласно эскизу, две кнопки должны быть расположены вертикально внутри этого фрейма, с btn_open вверху. Это можно сделать с помощью диспетчера геометрии .grid () или .pack () .На данный момент вы остановитесь на .grid () , так как с ним немного проще работать.

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

  1импорт tkinter as tk
 2
 3window = tk.Tk ()
 4window.title («Простой текстовый редактор»)
 5
 6window.rowconfigure (0, minsize = 800, weight = 1)
 7window.columnconfigure (1, minsize = 800, weight = 1)
 8
 9txt_edit = tk.Text (окно)
10fr_buttons = tk.Frame (окно)
11btn_open = tk.Кнопка (fr_buttons, text = "Открыть")
12btn_save = tk.Button (fr_buttons, text = "Сохранить как ...")
  

Вот расшифровка этого кода:

  • Строка 1 импортирует tkinter .
  • Строки 3 и 4 создают новое окно с заголовком «Простой текстовый редактор» .
  • Строки 6 и 7 устанавливают конфигурации строк и столбцов.
  • Строки с 9 по 12 создают четыре виджета, которые вам понадобятся для текстового поля, фрейма и кнопок открытия и сохранения.

Взгляните на строку 6 повнимательнее. Параметр minsize для .rowconfigure () установлен на 800 , а вес установлен на 1 :

  window.rowconfigure (0, minsize = 800, weight = 1)
  

Первый аргумент - 0 , который устанавливает высоту первой строки равной 800 пикселей и гарантирует, что высота строки увеличивается пропорционально высоте окна. В макете приложения всего одна строка, поэтому эти настройки применяются ко всему окну.

Давайте также более подробно рассмотрим строку 7. Здесь вы используете .columnconfigure () , чтобы установить атрибуты width и weight столбца с индексом от 1 до 800 и 1 , соответственно :

  window.columnconfigure (1, minsize = 800, weight = 1)
  

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

Теперь вы можете работать над макетом приложения. Сначала назначьте две кнопки кадру fr_buttons с помощью диспетчера геометрии .grid () :

  btn_open.grid (row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
btn_save.grid (строка = 1, столбец = 0, sticky = "ew", padx = 5)
  

Эти две строки кода создают сетку с двумя строками и одним столбцом в кадре fr_buttons , поскольку для обоих btn_open и btn_save свой атрибут master установлен на fr_buttons . btn_open помещается в первую строку и btn_save во вторую строку, так что btn_open появляется над btn_save в макете, как вы запланировали в своем эскизе.

У btn_open и btn_save атрибут sticky установлен на «ew» , что заставляет кнопки расширяться по горизонтали в обоих направлениях и заполнять весь фрейм. Это гарантирует, что обе кнопки имеют одинаковый размер.

Вы размещаете 5 пикселей отступа вокруг каждой кнопки, задав для параметров padx и pady значение 5. Только btn_open имеет вертикальное заполнение. Поскольку он находится сверху, вертикальное заполнение немного смещает кнопку вниз от верхней части окна и обеспечивает небольшой промежуток между ней и btn_save .

Теперь, когда fr_buttons выложено и готово к работе, вы можете настроить макет сетки для остальной части окна:

  fr_buttons.сетка (строка = 0, столбец = 0, липкий = "нс")
txt_edit.grid (строка = 0, столбец = 1, sticky = "nsew")
  

Эти две строки кода создают сетку с одной строкой и двумя столбцами для окна . Вы помещаете fr_buttons в первый столбец и txt_edit во второй столбец, так что fr_buttons появляется слева от txt_edit в макете окна.

Параметр sticky для fr_buttons установлен на "нс" , что заставляет весь фрейм расширяться по вертикали и заполнять всю высоту его столбца. txt_edit заполняет всю свою ячейку сетки, потому что вы устанавливаете для параметра sticky значение "nsew" , что заставляет его до расширяться во всех направлениях .

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

Отлично выглядит! Но пока он ничего не делает, поэтому вам нужно начать писать команды для кнопок. btn_open должен показать диалог открытия файла и позволить пользователю выбрать файл.Затем ему нужно открыть этот файл и установить текст txt_edit в его содержимое. Вот функция open_file () , которая делает именно это:

  1def open_file ():
 2 "" "Открыть файл для редактирования." ""
 3 путь к файлу = askopenfilename (
 4 filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")]
 5)
 6, если не путь к файлу:
 7 возврат
 8 txt_edit.delete ("1.0", т.к.END)
 9 с open (путь к файлу, "r") как input_file:
10 текст = входной_файл.читать()
11 txt_edit.insert (т.к.END, текст)
12 window.title (f "Простой текстовый редактор - {filepath}")
  

Вот разбивка этой функции:

  • Строки с 3 по 5 используют диалоговое окно askopenfilename из модуля tkinter.filedialog для отображения диалогового окна открытия файла и сохранения выбранного пути к файлу в filepath .
  • Строки 6 и 7 проверяют, закрывает ли пользователь диалоговое окно или нажимает ли Отмена .Если это так, то путь к файлу будет None , и функция вернет без выполнения какого-либо кода для чтения файла и установки текста txt_edit .
  • Строка 8 очищает текущее содержимое txt_edit с помощью .delete () .
  • Строки 9 и 10 открывают выбранный файл, а .read () его содержимое перед сохранением текста в виде строки.
  • Строка 11 назначает строку text на txt_edit с использованием .Вставить () .
  • Строка 12 устанавливает заголовок окна так, чтобы он содержал путь к открытому файлу.

Теперь вы можете обновить программу так, чтобы btn_open вызывала open_file () при каждом нажатии. Чтобы обновить программу, вам нужно сделать несколько вещей. Сначала импортируйте askopenfilename () из tkinter.filedialog , добавив следующий импорт в начало вашей программы:

  импорт tkinter as tk
из tkinter.filedialog import askopenfilename

окно = tk.Tk ()
window.title ("Простой текстовый редактор")

window.rowconfigure (0, минимальный размер = 800, вес = 1)
window.columnconfigure (1, minsize = 800, weight = 1)

txt_edit = tk.Text (окно)
fr_buttons = tk.Frame (окно)
btn_open = tk.Button (fr_buttons, text = "Открыть")
btn_save = tk.Button (fr_buttons, text = "Сохранить как ...")
  

Затем добавьте определение open_file () чуть ниже операторов импорта:

  импорт tkinter as tk
из tkinter.filedialog import askopenfilename

def open_file ():
    "" "Открыть файл для редактирования." ""
    filepath = askopenfilename (
        filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")]
    )
    если не путь к файлу:
        возвращаться
    txt_edit.delete ("1.0", т.к.END)
    с open (путь к файлу, "r") как input_file:
        текст = input_file.read ()
        txt_edit.insert (т.к.END, текст)
    window.title (f "Простой текстовый редактор - {filepath}")

окно = tk.Tk ()
window.title ("Простой текстовый редактор")

window.rowconfigure (0, минимальный размер = 800, вес = 1)
окно.columnconfigure (1, minsize = 800, weight = 1)

txt_edit = tk.Text (окно)
fr_buttons = tk.Frame (окно)
btn_open = tk.Button (fr_buttons, text = "Открыть")
btn_save = tk.Button (fr_buttons, text = "Сохранить как ...")
  

Наконец, установите для атрибута command btn_opn значение open_file :

  импорт tkinter as tk
из tkinter.filedialog импортировать askopenfilename

def open_file ():
    "" "Открыть файл для редактирования." ""
    filepath = askopenfilename (
        filetypes = [("Текстовые файлы", "*.txt "), (" Все файлы "," *. * ")]
    )
    если не путь к файлу:
        возвращаться
    txt_edit.delete ("1.0", т.к.END)
    с open (путь к файлу, "r") как input_file:
        текст = input_file.read ()
        txt_edit.insert (т.к.END, текст)
    window.title (f "Простой текстовый редактор - {filepath}")

окно = tk.Tk ()
window.title ("Простой текстовый редактор")

window.rowconfigure (0, минимальный размер = 800, вес = 1)
window.columnconfigure (1, minsize = 800, weight = 1)

txt_edit = tk.Text (окно)
fr_buttons = tk.Frame (окно)
btn_open = tk.Button (fr_buttons, text = "Открыть", command = open_file)
btn_save = тк.Кнопка (fr_buttons, text = "Сохранить как ...")
  

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

Когда работает btn_open , пора поработать над функцией для btn_save . Это должно открыть диалоговое окно сохранения файла , чтобы пользователь мог выбрать, где он хотел бы сохранить файл. Для этого вы воспользуетесь диалоговым окном asksaveasfilename в модуле tkinter.filedialog . Эта функция также должна извлечь текст, который сейчас находится в txt_edit , и записать его в файл в выбранном месте.Вот функция, которая делает именно это:

  1def save_file ():
 2 "" "Сохранить текущий файл как новый." ""
 3 путь к файлу = asksaveasfilename (
 4 defaultextension = "txt",
 5 filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")],
 6)
 7, если не путь к файлу:
 8 возврат
 9 с open (путь к файлу, "w") в качестве output_file:
10 text = txt_edit.get ("1.0", т.к.END)
11 output_file.write (текст)
12 window.title (f "Простой текстовый редактор - {filepath}")
  

Вот как работает этот код:

  • Строки с 3 по 6 используют диалоговое окно asksaveasfilename , чтобы получить от пользователя желаемое место сохранения.Выбранный путь к файлу сохраняется в переменной filepath .
  • Строки 7 и 8 проверяют, закрывает ли пользователь диалоговое окно или нажимает кнопку Отмена . Если это так, то путь к файлу будет Нет , и функция вернется без выполнения какого-либо кода для сохранения текста в файл.
  • Строка 9 создает новый файл по выбранному пути к файлу.
  • Строка 10 извлекает текст из txt_edit с .get () и присваивает его переменной text .
  • Строка 11 записывает текст в выходной файл.
  • Строка 12 обновляет заголовок окна, чтобы в заголовке окна отображался новый путь к файлу.

Теперь вы можете обновить программу так, чтобы btn_save вызывал save_file () при нажатии. Опять же, есть несколько вещей, которые вам нужно сделать, чтобы обновить программу. Во-первых, import спрашиваетaveasfilename () из tkinter.filedialog , обновив импорт в верхней части скрипта, например:

  импорт tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file ():
    "" "Открыть файл для редактирования." ""
    filepath = askopenfilename (
        filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")]
    )
    если не путь к файлу:
        возвращаться
    txt_edit.delete (1.0, т.к.END)
    с open (путь к файлу, "r") как input_file:
        текст = input_file.read ()
        txt_edit.вставить (tk.END, текст)
    window.title (f "Простой текстовый редактор - {filepath}")

окно = tk.Tk ()
window.title ("Простой текстовый редактор")
window.rowconfigure (0, минимальный размер = 800, вес = 1)
window.columnconfigure (1, minsize = 800, weight = 1)

txt_edit = tk.Text (окно)
fr_buttons = tk.Frame (окно, рельеф = tk.RAISED, bd = 2)
btn_open = tk.Button (fr_buttons, text = "Открыть", command = open_file)
btn_save = tk.Button (fr_buttons, text = "Сохранить как ...")

btn_open.grid (row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
btn_save.grid (строка = 1, столбец = 0, sticky = "ew", padx = 5)

fr_buttons.сетка (строка = 0, столбец = 0, липкий = "нс")
txt_edit.grid (строка = 0, столбец = 1, sticky = "nsew")

window.mainloop ()
  

Затем добавьте определение save_file () чуть ниже определения open_file () :

  импорт tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file ():
    "" "Открыть файл для редактирования." ""
    filepath = askopenfilename (
        filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")]
    )
    если не путь к файлу:
        возвращаться
    txt_edit.удалить (1.0, т.к.END)
    с open (путь к файлу, "r") как input_file:
        текст = input_file.read ()
        txt_edit.insert (т.к.END, текст)
    window.title (f "Простой текстовый редактор - {filepath}")

def save_file ():
    "" "Сохранить текущий файл как новый." ""
    filepath = asksaveasfilename (
        defaultextension = "txt",
        filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")],
    )
    если не путь к файлу:
        возвращаться
    с open (путь к файлу, "w") как output_file:
        текст = txt_edit.get (1.0, т.к.END)
        output_file.write (текст)
    window.title (f "Простой текстовый редактор - {filepath}")

окно = tk.Tk ()
window.title ("Простой текстовый редактор")
window.rowconfigure (0, минимальный размер = 800, вес = 1)
window.columnconfigure (1, minsize = 800, weight = 1)

txt_edit = tk.Text (окно)
fr_buttons = tk.Frame (окно, рельеф = tk.RAISED, bd = 2)
btn_open = tk.Button (fr_buttons, text = "Открыть", command = open_file)
btn_save = tk.Button (fr_buttons, text = "Сохранить как ...")

btn_open.grid (row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
btn_save.сетка (row = 1, column = 0, sticky = "ew", padx = 5)

fr_buttons.grid (строка = 0, столбец = 0, липкий = "нс")
txt_edit.grid (строка = 0, столбец = 1, sticky = "nsew")

window.mainloop ()
  

Наконец, установите для атрибута command btn_save значение save_file :

  импорт tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file ():
    "" "Открыть файл для редактирования." ""
    filepath = askopenfilename (
        filetypes = [("Текстовые файлы", "*.txt "), (" Все файлы "," *. * ")]
    )
    если не путь к файлу:
        возвращаться
    txt_edit.delete (1.0, т.к.END)
    с open (путь к файлу, "r") как input_file:
        текст = input_file.read ()
        txt_edit.insert (т.к.END, текст)
    window.title (f "Простой текстовый редактор - {filepath}")

def save_file ():
    "" "Сохранить текущий файл как новый." ""
    filepath = asksaveasfilename (
        defaultextension = "txt",
        filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")],
    )
    если не путь к файлу:
        возвращаться
    с open (путь к файлу, "w") как output_file:
        текст = txt_edit.получить (1.0, т.к.END)
        output_file.write (текст)
    window.title (f "Простой текстовый редактор - {filepath}")

окно = tk.Tk ()
window.title ("Простой текстовый редактор")
window.rowconfigure (0, минимальный размер = 800, вес = 1)
window.columnconfigure (1, minsize = 800, weight = 1)

txt_edit = tk.Text (окно)
fr_buttons = tk.Frame (окно, рельеф = tk.RAISED, bd = 2)
btn_open = tk.Button (fr_buttons, text = "Открыть", command = open_file)
btn_save = tk.Button (fr_buttons, text = "Сохранить как ...", command = save_file)

btn_open.grid (row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
btn_save.сетка (row = 1, column = 0, sticky = "ew", padx = 5)

fr_buttons.grid (строка = 0, столбец = 0, липкий = "нс")
txt_edit.grid (строка = 0, столбец = 1, sticky = "nsew")

window.mainloop ()
  

Сохраните файл и запустите его. Теперь у вас есть минималистичный, но полнофункциональный текстовый редактор!

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

Вот полный сценарий для справки:

  импорт tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file ():
    "" "Открыть файл для редактирования."" "
    filepath = askopenfilename (
        filetypes = [("Текстовые файлы", "* .txt"), ("Все файлы", "*. *")]
    )
    если не путь к файлу:
        возвращаться
    txt_edit.delete (1.0, т.к.END)
    с open (путь к файлу, "r") как input_file:
        текст = input_file.read ()
        txt_edit.insert (т.к.END, текст)
    window.title (f "Простой текстовый редактор - {filepath}")

def save_file ():
    "" "Сохранить текущий файл как новый." ""
    filepath = asksaveasfilename (
        defaultextension = "txt",
        filetypes = [("Текстовые файлы", "*.txt "), (" Все файлы "," *. * ")],
    )
    если не путь к файлу:
        возвращаться
    с open (путь к файлу, "w") как output_file:
        text = txt_edit.get (1.0, т.к.END)
        output_file.write (текст)
    window.title (f "Простой текстовый редактор - {filepath}")

окно = tk.Tk ()
window.title ("Простой текстовый редактор")
window.rowconfigure (0, минимальный размер = 800, вес = 1)
window.columnconfigure (1, minsize = 800, weight = 1)

txt_edit = tk.Text (окно)
fr_buttons = tk.Frame (окно, рельеф = tk.RAISED, bd = 2)
btn_open = tk.Button (fr_buttons, text = "Открыть", command = open_file)
btn_save = тк.Кнопка (fr_buttons, text = "Сохранить как ...", command = save_file)

btn_open.grid (row = 0, column = 0, sticky = "ew", padx = 5, pady = 5)
btn_save.grid (строка = 1, столбец = 0, sticky = "ew", padx = 5)

fr_buttons.grid (строка = 0, столбец = 0, липкий = "нс")
txt_edit.grid (строка = 0, столбец = 1, sticky = "nsew")

window.mainloop ()
  

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

Заключение

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

Из этого руководства вы узнали несколько важных концепций Tkinter:

  • Как работать с виджетами
  • Как управлять макетом приложения с помощью диспетчеров геометрии
  • Как сделать ваши приложения интерактивными
  • Как использовать пять основных виджетов Tkinter ( Label , Button , Entry , Text и Frame )

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

Дополнительные ресурсы

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

Tkinter Ссылки

Вот некоторые официальные ресурсы, на которые стоит обратить внимание:

  • Официальный учебник Python Tkinter умеренно подробно описывает модуль Python Tkinter.Он написан для более продвинутых разработчиков Python и не лучший ресурс для новичков.
  • Справочник по Tkinter 8.5: графический интерфейс для Python - это обширный справочник, охватывающий большую часть модуля Tkinter. Он исчерпывающий, но написан в справочном стиле без комментариев и примеров.
  • Справочник по командам Tk является исчерпывающим руководством по командам в библиотеке Tk. Он написан для языка Tcl, но отвечает на многие вопросы о том, почему все работает так, как в Tkinter.В официальных документах Python есть раздел о сопоставлении базового Tk с Tkinter, который незаменим при чтении документа Tk Commands.

Дополнительные виджеты

В этом руководстве вы узнали о виджетах Label , Button , Entry , Text и Frame . В Tkinter есть несколько других виджетов, все из которых необходимы для создания реальных приложений. Вот несколько ресурсов, чтобы продолжить изучение виджетов:

  • TkDocs Tkinter Tutorial - это довольно подробное руководство по Tk, базовой библиотеке кода, используемой Tkinter.Примеры представлены на Python, Ruby, Perl и Tcl. Вы можете найти несколько примеров виджетов, помимо рассмотренных здесь, в двух разделах:
  • В официальной документации Python есть три раздела, посвященные дополнительным виджетам:

Распространение приложений

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

Другие фреймворки графического интерфейса

Tkinter - не единственный ваш выбор для среды графического интерфейса Python.Если Tkinter не соответствует потребностям вашего проекта, обратите внимание на другие фреймворки:

Дизайнеров виджетов - Разработка Sitefinity CMS

Обзор

У виджетов

есть свойства, которые вы устанавливаете для каждого виджета и которые изменяют функциональность конкретного виджета. Если вы хотите отображать сообщения в блоге с помощью виджета Blog posts , вам необходимо указать виджету, какой блог использовать, сколько сообщений отображать на одной странице и как их отображать. Вы настраиваете виджет через его конструктор виджетов, то есть в режиме виджетов Edit .Кроме того, вы можете предопределить свойства любого типа виджета, чтобы после того, как он был помещен на страницу, виджет был предварительно настроен и готов к использованию. Для получения дополнительной информации см. Установка предопределенных значений для свойств виджета.

Вы можете настроить виджет двумя способами:

  • Расширенный режим
    В режиме Расширенный виджета отображается автоматически созданный список свойств. Когда вы создаете виджет, вы должны объявить все свойства, которые могут быть изменены, как общедоступные.Sitefinity CMS автоматически генерирует список свойств, которые можно напрямую изменять в браузере. Sitefinity CMS сохраняет все значения, которые вы изменяете, в базе данных. В этом списке свойств в режиме Advanced перечислены все общедоступные свойства.
  • Простой режим - конструктор виджетов
    Другой способ настройки свойств виджета - использование конструктора виджетов, который называется Простой режим . Не все встроенные виджеты имеют конструкторы виджетов. В конструкторе виджетов вы можете создать любой желаемый пользовательский интерфейс, чтобы сделать настройку виджета более удобной для пользователя.

Чтобы открыть и использовать конструктор виджетов:

  1. В серверной части Sitefinity CMS откройте для редактирования страницу, содержащую виджет.
  2. Нажмите кнопку Изменить в правом верхнем углу виджета.
    Появится дизайнер виджетов. Если у виджета нет дизайнера, появляется расширенный режим настройки виджета. Расширенный режим содержит редактируемый список всех общедоступных свойств виджета.

Для переключения между режимами Simple и Advanced используйте кнопку в правом нижнем углу виджета.

Технический обзор

С Sitefinity CMS вы можете работать со средой конструктора виджетов, основанной на клиентских технологиях, таких как HTML, JavaScript, AngularJS и Bootstrap. Вы также можете воспользоваться библиотекой многократно используемых клиентских компонентов.

А конструктор виджетов - это модуль AngularJs, в котором каждое представление имеет контроллер AngularJs. По умолчанию Sitefinity CMS предоставляет все общедоступные свойства, доступные в контроллере виджета, поэтому их можно редактировать с помощью дизайнера виджета.Все эти свойства доступны в $ scope.properties контроллера AngularJs. Когда вы редактируете любое из этих свойств и сохраняете конструктор, ваши изменения сохраняются в базе данных. Использование $ scope.properties позволяет связывать свойства виджета непосредственно в шаблоне представления дизайнера без написания дополнительного кода. Объект службы свойств позволяет получить все свойства контроллера виджета и использовать их в контроллере конструктора виджетов.

Контроллеры

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

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

При работе с конструкторами виджетов по умолчанию или их расширении и представлениях конструктора используйте только клиентские компоненты:

  • Автоматически создается файл JSON с регистрацией скриптов.
  • Файл JavaScript со всеми зависимостями Angular создается автоматически.
  • Если других представлений дизайнера с явно установленным приоритетом не существует, приоритет представления дизайнера устанавливается на 1 .

ПРИМЕЧАНИЕ. Если у вас есть файлы JSON или JavaScript, соответствующие соглашениям об именах, даже если эти файлы пусты, автоматическая регистрация сценариев или регистрация зависимостей не происходит.
Для получения дополнительной информации см. Клиентские компоненты.

Наборы виджетов - документация Profound Logic

Эта функция доступна только в Profound UI Version 6 Fix Pack 0.0 и новее.

Начиная с Profound UI Version 6, Visual Designer включает возможность группировать и сохранять виджеты в настраиваемые наборы. Панель наборов виджетов находится в левой части конструктора.

Некоторые наборы по умолчанию поставляются с Profound UI. Чтобы выбрать набор виджетов, щелкните имя набора на панели «Наборы виджетов».Чтобы использовать виджеты в наборе, перетащите виджет из выбранного набора на холст «Дизайн».

Создать новый набор виджетов так же просто, как нажать кнопку Добавить набор на панели инструментов панели «Наборы виджетов».Затем появится диалоговое окно с запросом ввода имени для нового набора.

Чтобы переименовать набор виджетов, выберите набор и нажмите кнопку Переименовать набор на панели инструментов панели «Наборы виджетов». Затем введите новое имя набора в диалоговом окне «Переименовать набор».

Чтобы удалить набор виджетов, выберите набор и нажмите кнопку Удалить набор на панели инструментов панели «Наборы виджетов».

Мы понимаем, что некоторые пользователи могут захотеть, чтобы их настраиваемый набор виджетов находился в верхней части списка наборов виджетов, поэтому мы предоставили простой способ изменить их порядок! Используя Переместить набор и Переместить вниз. Кнопки , расположенные на панели инструментов «Наборы виджетов», позволяют перемещать выбранный в данный момент набор виджетов вверх или вниз по списку.

Виджеты можно добавить из панели инструментов «Все виджеты» в набор, щелкнув виджет правой кнопкой мыши и выбрав нужный набор. Это добавит виджет в выбранный набор с настройками виджета по умолчанию.

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

Виджет будет сохранен со всеми свойствами, установленными в настоящее время в окне «Свойства», включая любые привязки полей.

Переименование виджетов

Чтобы переименовать виджет в наборе, щелкните виджет правой кнопкой мыши и выберите «Переименовать». Затем введите новое имя в диалоговом окне «Переименовать виджет».

Изменение значков виджета

Чтобы изменить значок виджета, щелкните виджет правой кнопкой мыши и выберите «Изменить значок».Затем выберите новый значок в диалоговом окне выбора значка.

Автоматическое создание имен связанных полей

Когда виджеты со связанными полями добавляются в набор, информация привязки полей сохраняется, так что при перетаскивании виджета на холст все имена связанных полей / values ​​/ types и информация о форматировании будут установлены так же, как и при сохранении виджета в набор. Это может привести к дублированию имен полей при перетаскивании виджетов на холст.Чтобы автоматически генерировать имена связанных полей при перетаскивании виджета на холст, щелкните виджет правой кнопкой мыши и выберите «Автоматически создавать имена полей».

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

Изменение порядка виджетов в наборе

По умолчанию виджеты будут отображаться в том порядке, в котором они были добавлены в набор. Чтобы изменить порядок виджетов в наборе, нажмите кнопку «Изменить порядок виджетов» на панели инструментов панели «Наборы виджетов».Затем перетащите виджеты в нужные места.

По завершении изменения порядка нажмите кнопку «Изменить порядок виджетов» еще раз, чтобы выйти из режима изменения порядка.

Удаление виджетов

Чтобы удалить виджет из набора, щелкните виджет правой кнопкой мыши и выберите «Удалить».

Хранение данных наборов виджетов

Наборы виджетов первоначально загружаются из файла данных по умолчанию, который поставляется с установкой Profound UI.Файл данных набора виджетов по умолчанию хранится в DOCUMENT_ROOT / profoundui / proddata / widget_sets.json. Данные набора виджетов загружаются в локальную область памяти браузера, и любые изменения данных набора виджетов выполняются только в локальном хранилище. Настройки можно сохранить на сервере, нажав кнопку «Сохранить наборы на сервере» на панели инструментов панели «Наборы виджетов».

Данные пользовательского набора виджетов хранятся в DOCUMENT_ROOT / profoundui / userdata / widget_sets.json. Чтобы заменить содержимое локального хранилища содержимым файла данных на сервере, нажмите кнопку «Загрузить наборы с сервера» на панели инструментов панели «Наборы виджетов».

Скрыть виджеты WordPress на мобильном устройстве

Скрыть виджеты нижнего колонтитула на мобильных устройствах WordPress

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

Как скрыть виджеты WordPress на мобильных устройствах, Зачем скрывать виджеты WordPress на мобильных устройствах? Скачайте бесплатно Widget Options. На панели управления WordPress нажмите «Виджеты» на вкладке «Внешний вид». Щелкните виджет, чтобы открыть параметры, и перейдите на вкладку «Устройства» (значок «мобильный телефон»). В меню «Скрыть / показать» выберите «Скрыть на отмеченных устройствах».Давайте продолжим и отредактируем виджет поиска, щелкнув имя виджета. В настройках виджета вы увидите новый раздел, добавленный подключаемым модулем параметров виджета. Здесь просто нажмите на маленький значок мобильного телефона, затем выберите, на каком устройстве или устройствах вы хотите скрыть виджет.

Скрыть некоторые виджеты нижнего колонтитула на мобильном устройстве, Если вы используете Elementor для разработки нижнего колонтитула, вы можете отключить виджет в мобильном представлении в настройках виджета на Elementor> вкладка «Дополнительно»> В настройках виджета вы см. новый раздел, добавленный подключаемым модулем параметров виджета.Здесь просто нажмите на маленький значок мобильного телефона, затем выберите, на каком устройстве или устройствах вы хотите скрыть виджет. Не забудьте нажать кнопку «Сохранить», чтобы сохранить изменения. Теперь проверьте свой сайт с помощью мобильного устройства.

Плагин для виджетов WordPress

25 лучших плагинов WordPress для боковых панелей и виджетов (2021 г.), 25 лучших плагинов WordPress для боковых панелей и виджетов (2021 г.) · 1. Пакет виджетов SiteOrigin (БЕСПЛАТНО) · 2. Боковые панели с учетом содержимого (БЕСПЛАТНО) · 3.Текстовый виджет - один из наиболее часто используемых виджетов WordPress, который поставляется с каждой установкой WordPress. Он позволяет пользователям добавлять текст, видео, изображения, настраиваемые списки и многое другое на свои сайты WordPress. Чтобы использовать текстовый виджет WordPress:

Параметры виджета - Добавить контекст в виджеты WordPress, С помощью подключаемого модуля параметров виджета вы можете легко назначать видимость страниц и ограничивать каждый виджет на разных страницах, сообщениях, настраиваемых типах сообщений, категориях, тегах и настраиваемых Widget Areas - полезный плагин премиум-класса для WordPress, который позволяет создавать и добавлять пользовательские боковые панели или области виджетов к любому сообщению или странице.Просто создайте новую область виджетов и выберите боковую панель, которую вы хотите заменить - это очень просто. Начать работу не может быть проще.

Плагины, отнесенные к категории виджетов, Тег плагина: виджет Создавайте адаптивные макеты страниц, используя виджеты, которые вы знаете и любите, с помощью этого простого перетаскивания… Виджет визуального редактора для WordPress. Хотя я не уверен, что означает название этого плагина, Q2W3 Fixed Widget for WordPress - это отличный плагин, который позволяет вам сделать любой из ваших других виджетов «липким».То есть виджет будет «прилипать» к странице, даже когда посетители прокручивают страницу вниз.

Виджеты WordPress для мобильных устройств

Как скрыть виджет WordPress на мобильных устройствах (легко для начинающих), В настройках виджета вы увидите новый раздел, добавленный подключаемым модулем параметров виджета. Здесь просто щелкните небольшой значок мобильного устройства, затем выберите «Перейти к внешнему виду»> «Настроить» на экранах администрирования WordPress и щелкните меню виджетов в настройщике тем. Или перейдите в Внешний вид> Виджеты на экранах администрирования WordPress.Откройте боковую панель, на которую вы хотите добавить текстовый виджет. Найдите текстовый виджет в списке виджетов.

Как скрыть виджеты WordPress на мобильных устройствах, Шаг 1. Загрузите бесплатно параметры виджета. Нажмите «Плагины> Добавить новый» на панели инструментов WordPress, затем на странице «Добавить плагины» введите «Бесплатная версия WordPress Mobile Pack имеет широкий спектр функций и простой процесс настройки вашего мобильного веб-сайта. Он пытается максимально имитировать приложение, предлагая пользователям сохранить мобильный веб-сайт на своем домашнем экране для последующего использования.

10 лучших мобильных плагинов WordPress для 100% отзывчивости, Если вы используете виджеты для своих страниц и сообщений, вы можете тратить ценное веб-пространство, когда речь идет о посетителях, приходящих с мобильного устройства. Виджеты - это динамический контент, который часто добавляется на боковую панель или нижний колонтитул веб-сайта. Иногда виджет может хорошо выглядеть на настольных / портативных устройствах, но вы можете скрыть его на мобильных устройствах. В этой статье мы покажем вам, как условно скрыть виджет WordPress на мобильных устройствах (без написания кода).

Добавить виджет Плагин WordPress

Виджеты WordPress, Перейдите в раздел «Внешний вид»> «Настроить» на экранах администрирования WordPress. Щелкните меню виджетов в настройщике темы, чтобы получить доступ к экрану настройки виджета. Щелкните стрелку вниз в области виджетов, чтобы вывести список уже зарегистрированных виджетов. Нажмите кнопку «Добавить виджет» в нижней части боковой панели. Самый простой - просто перетащить их на боковую панель. Вы также можете щелкнуть заголовок виджета из списка доступных виджетов.WordPress покажет вам список боковых панелей, на которые вы можете добавить этот виджет. Просто выберите боковую панель, на которую вы хотите добавить виджет, а затем нажмите кнопку «Добавить виджет», чтобы добавить его.

Параметры виджета - Добавить контекст в виджеты WordPress, Загрузить полный каталог в каталог wp-content / plugins · Активируйте плагин на странице администрирования плагина · Перейдите в Настройки> Параметры виджета и добавьте Теперь, плагины виджетов WordPress входят в два варианта: специальные плагины для виджетов и общие плагины WordPress с поддержкой виджетов.В сегодняшнем списке мы будем смешивать и сопоставлять 25 бесплатных и премиальных плагинов из обеих категорий, поскольку мы запускаем лучшие плагины WordPress для добавления новых виджетов и улучшения боковых панелей.

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

Условный виджет WordPress

Условные виджеты - плагин WordPress, Описание. ВАЖНО: при обновлении установки MultiSite с Conditional Widgets 1.x до 2.x вам необходимо посетить панель управления каждого сайта, чтобы этот плагин добавил форму к каждому виджету на панели Widgets, которая позволяет пользователям выбрать, на каких страницах и / или в каких категориях виджет будет отображаться или скрыт.Для каждого виджета вы можете выбрать критерии: ПОКАЗАТЬ или СКРЫТЬ виджет в зависимости от ряда категорий.

Управление видимостью виджетов WordPress с помощью условных виджетов Условные виджеты - это плагин от Extension (Джейсон Лемахье и Кевин Грэм), который дает вам возможность показывать или скрывать ваши виджеты с большим контролем, чем WordPress позволяет легко добавлять виджеты на ваш сайт. боковая панель, нижний колонтитул и другие области, которые поддерживает ваша тема. Однако вы не захотите отображать все виджеты на каждой странице.Эти 5 подключаемых модулей условных виджетов могут помочь: Divi Builder построитель страниц перетаскивания для WP

Условные виджеты - Справка WordPress, Вы можете добавить условное содержимое WordPress к своим виджетам с помощью подключаемого модуля Widget Logic. Это руководство покажет вам, как использовать плагин и сегменты кода. Условные меню - это простой, но полезный плагин WordPress от Themify, который позволяет вам менять местами меню в теме в соответствии с определенными условиями. Короче говоря, у вас могут быть разные меню в разных сообщениях, страницах, категориях, страницах архива и т. Д.Он работает с любой темой WordPress, в которой используется стандартная функция меню WordPress. Как это использовать

Плагин WordPress для виджетов отображения

Плагин WordPress для отображения виджетов - Strategy11, Перейдите в «Внешний вид» -> «Виджеты», и когда вы развернете свои виджеты для их редактирования, вы увидите дополнительные флажки на каждом виджете. Автор сообщения. James With Widget Options Plugin, вы можете легко назначать видимость страниц и ограничивать каждый виджет на разных страницах, сообщениях, настраиваемых типах сообщений, категориях, тегах и настраиваемых таксономиях.Отображение или скрытие виджетов на мобильных устройствах, размеры экрана рабочего стола и / или планшета

Параметры виджета - Добавить контекст в виджеты WordPress, С помощью подключаемого модуля параметров виджета вы можете легко назначать видимость страниц и ограничивать каждую из них. Упрощенная навигация по сайту. Перейдите в «Внешний вид»> «Настроить» на экранах администрирования WordPress и щелкните меню виджетов в настройщике тем. Или перейдите в Внешний вид> Виджеты на экранах администрирования WordPress.Откройте боковую панель, на которую вы хотите добавить текстовый виджет. Найдите текстовый виджет в списке виджетов.

[Display Widgets] Support, Support »Plugin: Display Widgets. Искать: Искать в форумах. или Войдите в систему. Какие меры следует предпринять после удаления виджета дисплея? Начато: solidcake. Установите и активируйте плагин. Отредактируйте страницу, которую хотите добавить. Заполните отрывок и выберите избранное изображение на этой странице. Выберите «Внешний вид»> «Виджеты» или «Настройка»> «Виджеты».

Показать скрыть боковую панель WordPress

Скрыть или удалить боковую панель с любой страницы в WordPress, Удаление боковой панели со статической страницы · Используйте любой текстовый редактор и создайте новый файл. · Подключите свою учетную запись хостинга с помощью FTP-клиента. · Темы Find / wp- WordPress состоят из разных шаблонов, поэтому вам нужно будет отредактировать все шаблоны, в которых отображается боковая панель. Чтобы найти файл, см. Наше руководство по иерархии шаблонов WordPress. Например, в типичной теме WordPress вам может потребоваться отредактировать index.php, page.php, single.php, archive.php, home.php и так далее.

Как удалить боковую панель WordPress с любой страницы / сообщения, К сожалению, удаление боковой панели в WordPress не совсем так. Затем мы покажем вам различные методы, которые вы можете использовать, чтобы избавиться от вашей. Лучший плагин WordPress для скрытия боковых панелей Сначала нам нужно установить плагин, который позволит удалять боковые панели с любой страницы без использования кода: Content Aware Sidebars - лучший и самый популярный плагин WordPress для отображения и скрытия разных боковых панелей на разных страницах. типы контента.

Как удалить боковую панель в WordPress, Итак, нет другого способа скрыть боковую панель в WordPress, кроме использования атрибутов страницы? И если Атрибуты страницы не отображаются в Итак, для этого на самом деле есть способ скрыть боковую панель WordPress, не удаляя ее, и вы также можете сделать это для определенных страниц - и все это одним нажатием кнопки, ничего не устанавливая. Сначала перейдите в визуальный редактор WordPress для той конкретной страницы или публикации, с которой вы хотите удалить боковую панель.

WordPress скрыть боковую панель на мобильном устройстве

Как WordPress: удалить боковую панель в мобильном представлении (учебное пособие), Мы предлагаем вам краткое руководство по улучшению вашего мобильного взаимодействия с пользователем путем удаления боковой панели с вашего мобильного веб-сайта. Используйте медиа-запрос, чтобы скрыть боковую панель на экранах тех размеров, на которых вы не хотите, чтобы она отображалась. Вы можете поместить это в Внешний вид> Дополнительный CSS или создать дочернюю тему и поместить ее в таблицу стилей дочерней темы.

Как скрыть боковую панель на мобильных устройствах в WordPress, [Эта тема закрыта.] Привет. Есть ли какие-либо настройки для отключения боковой панели при просмотре сайта на мобильном устройстве? Другими словами,… Как скрыть виджет на мобильном устройстве в WordPress. В рамках этого руководства мы будем скрывать виджет поиска на нашем демонстрационном сайте, но вы можете использовать его, чтобы скрыть любой виджет WordPress. На нашем демонстрационном сайте виджет поиска появляется в верхней части боковой панели и отлично смотрится на рабочем столе. Однако на мобильном телефоне виджеты боковой панели отображаются под

Как отключить боковую панель на мобильных устройствах, Скрыть боковую панель на мобильных устройствах глобально.Это документ уровня разработчика. Если вы не знакомы с CSS и / или редактированием файлов, кодов и шаблонов, Как скрыть боковую панель на мобильных устройствах с помощью PHP Для этого вам нужно будет убедиться в нескольких вещах. Убедитесь, что вы используете дочернюю тему (в большинстве премиум-шаблонов она есть), установите ее поверх текущей темы, чтобы вы сохранили свои изменения даже при обновлении темы.

WordPress скрывает контент на мобильных устройствах

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

Скрыть раздел в мобильном представлении, [Эта ветка закрыта.] Здравствуйте, я ввел несколько логотипов в раздел проекта - потому что невозможно разместить изображения или код в ценах… Привет.Прежде всего: отличный плагин, спасибо! Можно ли по умолчанию скрыть контент только для мобильных посетителей? Таким образом, посетители настольных компьютеров видят оглавление, но для мобильных устройств оно по умолчанию скрыто.

Скрыть содержимое публикации WordPress на мобильных устройствах - лучший хост, Скрыть содержимое WordPress на мобильных телефонах. Вот код, который вы можете использовать: function not_mobile_shortcode ($ atts, $ content = '') {if Быстрое видео, показывающее, как использовать настройки видимости в теме Total WordPress для отображения и / или скрытия контента в зависимости от экрана пользователя размер.Это полезно для отображения или скрытия

Ошибка обработки файла SSI

Показать виджет на определенной странице Плагин WordPress

Как показать или скрыть виджеты на определенных страницах WordPress, Легко показать или скрыть виджеты WordPress на определенных страницах, в частности роли пользователей, классы на странице настроек плагина и использовать их позже для каждого виджета. Плагин Widget Context позволяет легко отображать / скрывать виджеты в выбранных записях, страницах, архивах и многом другом WordPress.Просто выберите в настройках разделы, в которых вы хотите показать или скрыть виджет. Вы также можете отображать или скрывать виджеты по определенным URL-адресам. Просто добавьте URL-адрес в настройках виджета.

Параметры виджета - Добавить контекст в виджеты WordPress, Это зависит от того, где вы хотите показать виджет. Начнем с регистрации области виджетов (боковой панели): add_action ('wp_loaded' Показать или скрыть виджеты на определенных страницах WordPress С помощью плагина Widget Options вы можете легко назначать видимость страниц и ограничивать каждый виджет на разных страницах, сообщениях, настраиваемых типах сообщений, категориях , теги и пользовательские таксономии.Показать или скрыть виджеты на мобильных устройствах, на экранах компьютеров и / или планшетов

Как добавить определенный виджет только на 1 страницу ?, Большинство виджетов WordPress отображаются на каждой странице сайта. как отображать виджеты на определенных страницах WordPress с помощью плагина Widget Context. Плагин параметров виджета для отображения или скрытия виджетов на определенных страницах в WordPress Параметры виджета - еще один популярный плагин для отображения или скрытия виджетов на определенных страницах в WordPress. Он предлагает несколько функций для управления видимостью виджетов.Этот плагин добавляет опции под каждым виджетом для отображения или скрытия виджета на определенных страницах.

Ошибка обработки файла SSI

Шорткоды Widget Logic

Стандартные фрагменты кода для Widget Logic, Widget Logic - это плагин WordPress, который дает каждому виджету дополнительное поле управления под названием «Логика виджета», которое позволяет вам управлять страницами, которые Логика виджетов предназначена для виджетов. Условная wp_is_mobile () вернет true, если ваш пост / страница просматривается на мобильном устройстве.Например, допустим, у вас есть текстовый виджет. Пример 1: wp_is_mobile () Условие выше означает, что ваш конкретный текстовый виджет будет отображаться только для пользователя, который использует мобильное устройство для просмотра ваших сообщений / страниц.

Как использовать Widget Logic для WordPress, Widget Logic - это мощный плагин, который контролирует, где должен появляться виджет, и, наоборот, где не должен. Это отличный легкий шорткод Logic Hop, позволяющий легко добавлять мощную условную логику для персонализации контента одним нажатием кнопки.Шорткоды - это всего лишь шорткоды. Думайте об этом как о сокращении для написания реального кода. шорткоды вставляются в сообщение или страницу WordPress и заменяются соответствующим динамическим контентом.

Использование плагина Widget Logic для отображения и скрытия виджетов WordPress, Использование условных тегов с Widget Logic · Установите и активируйте плагин Widget Logic. · Перейдите к своим виджетам, возможно, в настройщике. · Выбор шорткодов позволяет добавлять динамические элементы, такие как контактная форма, таблицы и другие элементы, в область содержимого WordPress.Вы также можете использовать шорткоды в своих виджетах, чтобы добавить эти динамические элементы на боковые панели и другие области, готовые к работе с виджетами. Давайте посмотрим, как легко добавлять и использовать шорткоды в виджеты боковой панели WordPress.

Ошибка обработки файла SSI

Параметры виджета

Параметры виджета - Добавить контекст в виджеты WordPress, Описание. Управляйте виджетами на боковой панели больше! Назначьте различное содержимое на боковые панели, нижний колонтитул и любые виджеты боковой панели. Widget Options добавляет элегантности, а Widget Options - лучший плагин для управления виджетами WordPress.Мы создали наш плагин, чтобы сделать его самым полным и удобным в мире плагином для управления виджетами WordPress. Получить параметры виджета Теперь просмотреть видеотрейлер

[Параметры виджета, поддержка »Плагин: параметры виджета - Добавить контекст в виджеты WordPress. Искать: используйте логику для отображения виджета в определенных категориях и таксономиях. Параметры виджета добавляют элегантные и многофункциональные вкладки под каждым виджетом для полного управления и контроля видимости и внешнего вида каждого виджета. Widget Options, созданный на основе полной интеграции с WordPress, является наиболее полным в мире плагином для управления виджетами.

Параметры виджета, Как мне показать виджеты на определенной странице в WordPress? Widget Options - лучший, самый полный и удобный в мире плагин для управления виджетами WordPress.

Ошибка обработки файла SSI

Еще статьи

Подробнее о редакторе строк zsh и создании пользовательских виджетов «Serge Gebhardt (sgeb.io)

Содержание

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

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

И вы, наверное, научились ориентироваться в нем. Вы справились с типичными задачами, такими как
как: повторить ранее введенные команды, поиск в истории, перейти к
начало / конец строки, переместить на одно слово назад / вперед, удалить предыдущее слово,
удалить до конца строки и т. д.

Давайте подробнее рассмотрим редактор строк zsh и расширим его с помощью настраиваемых
функциональность. Чтобы быть еще быстрее и эффективнее. И для прохлады
курс.

Что такое раскладки клавиатуры?

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

По умолчанию в zsh настроены следующие раскладки:

  • emacs : эмуляция emacs
  • viins : эмуляция vi (режим вставки)
  • vicmd : эмуляция vi (командный режим)
  • isearch : режим инкрементального поиска
  • команда : режим чтения команды
  • .безопасный : резервная раскладка клавиатуры (не может быть удалена)

emacs - это режим по умолчанию в большинстве оболочек (таких как zsh, Bash и т. Д.), А также на
большинство приложений на основе readline. Горячие клавиши вроде + b , + f ,
+ a , + e и + w прямо из эмуляции emacs. В
Режим эмуляции emacs включается с помощью bindkey -e .

viins и vicmd основаны на vi и будут знакомы людям, работающим в
vim.Чтобы переключиться из режима вставки в командный режим, нажмите esc . Чтобы переключиться с
командный режим для режима вставки, нажмите i . Режим эмуляции vi обычно запускается в
режим вставки и включается с помощью bindkey -v .

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

Остальные раскладки ( isearch , command и .safe ) имеют меньшее значение.
на данный момент и не будет здесь рассматриваться.

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

Большинство людей не меняют раскладку клавиатуры и оставляют ее на emacs . Emacs
эмуляция по умолчанию во многих текстовых средах и
с ним вы будете эффективны в приложениях, которые не предлагают
менее популярная эмуляция vi.Некоторые графические интерфейсы также поддерживают (частично) эмуляцию emacs,
например, система ввода текста в Mac OS X.

Alt, Meta и Escape

Вы могли видеть разные варианты написания одного и того же
ярлык. Например, обратное слово часто записывается как + b ,
\ eb , + b , + b или M-b . Все они одинаковы. В зависимости от
настройки вашего терминала, вы можете ввести + b или, альтернативно, нажать esc и
затем b .Они фактически соответствуют одному и тому же ключевому коду, а именно \ eb . Следовательно
разные варианты написания. - это пережиток старых времен и по сути
синоним \ e .

Я обычно пишу это как + b .

ZLE означает Zsh Line Editor, как описано ранее. Команда zle - это
специфическая функциональность, предоставляемая zle. Виджет zle является синонимом zle
команда - хотя и более конкретный термин, так как слово команда скорее
уже перегружен.В оставшейся части статьи я буду использовать виджет .

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

Список виджетов

Вы можете получить список всех доступных виджетов с помощью:

 % zle -la
.принять и удержать
.accept-and-infer-next-history
.accept-and-menu-complete
.accept-line
.accept-line-and-down-history
.accept-search
# [... еще много виджетов ...]
какая команда
дергать
янк-поп
zle-keymap-select
zle-line-finish
zle-line-init
  

Объяснение встроенных виджетов
здесь.

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

Запуск виджета

Обычно вызывается виджет:

  • через привязку клавиш (по сути, сочетание клавиш, например + ),
  • изнутри другого виджета через zle ,
  • из специального интерактивного виджета под названием execute-named-cmd .

Теперь, если виджет не привязан к какому-либо ключу, как выполнить execute-named-cmd
чтобы вызвать виджет?

Это зависит от вашей раскладки клавиатуры: + x в эмуляции emacs,
: в командном режиме эмуляции vi.

Вам будет предложено ввести имя виджета. Любой из перечисленных виджетов
по zle -la является допустимым вводом. Имена виджетов могут быть автозаполнены с помощью .

Вы можете запустить виджет execute-named-cmd из середины команды.
строки, вызовите определенный виджет и возобновите редактирование командной строки после
виджет запустился.

Например:

 % echo это тест [ + x]
выполнить: backward-kill- [<вкладка> - <вкладка>]
backward-kill-line backward-kill-word
выполнить: backward-kill-word []
% echo это
  

И да, вы даже можете вызвать execute-named-cmd изнутри
execute-named-cmd , хотя его практическое применение ограничено. M ).

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

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

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

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

  function _git-status {
    zle kill-целая линия
    zle -U "git status"
    zle accept-line
}
  

Функция читает довольно плавно. Обратите внимание, что zle вызывает
именованный виджет и zle -U добавляет строку в буфер строки в
Текущее местоположение.Я предпочитаю добавлять к имени функции знак подчеркивания
выделить его использование в качестве специальной функции оболочки, а не вызываемой пользователем
функция.

Следующий шаг - объявить эту функцию как виджет:

  zle -N _git-status
  

Наш новый виджет появляется в списке доступных виджетов:

 % zle -la | grep git-status
_git-status
  

Пользовательские функции также могут отменять встроенные виджеты:

  function _accept-line-with-echo {
    echo "Выполнение: $ BUFFER"
    зле.accept-line
}
zle -N accept-line _accept-line-with-echo
  

Переменная $ BUFFER содержит текущее содержимое zle. Есть еще много
интересно
переменные.

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

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

  bindkey '\ eg' _git-status
  

Вот и все, + g теперь выполняет git status , независимо от других
конфигурации в раскладке.

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

 % зле -л
принять и удерживать (_zsh_highlight_widget_accept-and-hold)
принять-и-предположить-следующую-историю (_zsh_highlight_widget_accept-and-infer-next-history)
принять-и-меню-завершено (_zsh_highlight_widget_accept-and-menu-complete)
строка приема (_zsh_highlight_widget_accept-line)
история приема-и-вниз (_zsh_highlight_widget_accept-line-and-down-history)
accept-search (_zsh_highlight_widget_accept-search)
аргумент-база (_zsh_highlight_widget_argument-base)
авто-суффикс-удаление (_zsh_highlight_widget_auto-suffix-remove)
# [... еще много виджетов ...]
  

Имя функции указано в скобках.

Список может содержать виджеты с параметром -C . Они используются для
автозаполнение. Это обширная тема, которую я не буду рассматривать в этой статье.
Пожалуйста, обратитесь к zsh
документация.

В предыдущих примерах мы видели сочетание определений функций и zsh
такие команды, как zle и bindkey . Куда вы их кладете, чтобы кастом
виджеты доступны в каждом экземпляре zsh?

Я обычно помещаю все инструкции, связанные с zle, в файл zle.sh и отправьте его на
zsh автозагрузка. Все сводится к следующему.

Файл ~ / .zsh / zle.zsh :

  # Привязать \ например к `git status`
function _git-status {
    zle kill-целая линия
    zle -U "git status"
    zle accept-line
}
zle -N _git-status
bindkey '\ eg' _git-status
  

Где-то в ~ / .zshrc :

  источник ~ / .zsh / zle.zsh
  

Фактический код немного сложнее и доступен в моем
точечные файлы.

Куда дальше?

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

Мы коснулись поверхности надуманными примерами. Но, надеюсь, этого достаточно
чтобы вы начали писать свои собственные виджеты и стали более эффективными в
с помощью редактора строк zsh.

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

Добавление действий к виджетам - ArcGIS Experience Builder

Для создания динамических и интерактивных приложений с помощью Experience Builder вы можете сделать следующее:

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

Действия с сообщениями

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

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

  • Триггеры от исходного виджета
  • Целевые объекты, которые отвечают на триггер (виджеты и платформа приложения)
  • Действия от целевого объекта, связанные с его предполагаемым использованием

Триггер исходного виджета может добавляется только один раз; однако вы можете добавить несколько действий для этого конкретного триггера с той же или другой цели.

Триггеры

Триггеры - это события, генерируемые исходным виджетом. После срабатывания триггера любые добавленные к нему действия выполняются в ответ. В следующей таблице описаны два триггера, которые в настоящее время поддерживаются:

Триггер Описание

Extent Changes

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

Изменения выбора записи

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

Записи созданы

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

Чтобы просмотреть доступные триггеры виджета, откройте его настройки и нажмите «Добавить триггер» на вкладке «Действие». В следующей таблице перечислены исходные виджеты и поддерживаемые ими триггеры:

Исходный виджет Изменения экстента Изменения выбора записи Созданные записи
9305 9309 930 918

Информация о характеристиках

Список

000

9306 9309 9309 930 930 9309

Таблица

Цели

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

Действия

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

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

8 9

Цель Действие * Триггер: изменения выбора записи Триггер: изменения степени Триггер: записи

Framework

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

Выбрать записи данных ** - пометьте записи как выбранные и поместите их в представление «Выбранные функции».

Виджет карты

Панорама - сохранение текущего масштаба и центрирование в указанном месте.

Увеличить до - увеличение до указанного места.

Вспышка. Вспышка объекта на карте.

Фильтр - оставьте только те функции, которые соответствуют условиям для просмотра в виджете карты.

Показать на карте - отображение созданных записей на карте как нового слоя.

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

** Выбранное состояние записи синхронизируется во всем приложении, и выбранные записи сохраняются в представлении данных «Выбранные функции». Виджеты могут иметь другую конфигурацию для отображения выбранной записи.

Действия с данными

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

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

  • Целевые объекты, которые получают набор данных (виджеты и платформа приложения)
  • Действия с данными, предоставляемые целевым объектом, выполняются только тогда, когда конечные пользователи выбирают

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

Все

8 выбранных

8 9309

Выбор

Все

Виджет источника Структура: Экспорт Карта: Увеличить до Карта: Панорама до Карта: Показать на карте Таблица: Посмотреть в таблице 9 8

Диаграмма

Все записи (статистические данные)

Н / Д

Н / Д

Н / Д

Все записи (статистические данные) 9

Информация о функциях

Выбор

Выбор

Выбор

Выбор

Выбор

Список

Выбор

Выбор

Запрос

Все записи (результат запроса)

Все записи (результат запроса)

Все записи (результат запроса)

Все записи (результат запроса)

записи (результат запроса)

Таблица

Все или выбранные записи

Выбор

Выбор

Выбор

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

Настроить взаимодействия

Настроить действия с сообщениями и данные на панели действий исходных виджетов.

Настроить действия с сообщениями

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

  1. На панели «Действие» на вкладке «Действие с сообщением» нажмите «Добавить триггер».
  2. Выберите триггер, выберите цель, добавьте и выберите действия.

    Некоторые действия, такие как «Фильтр», «Вспышка» и «Выбор записей данных», требуют дополнительной настройки. Параметры настройки действия появляются, когда вы выбираете действие. Каждый триггер можно добавить только один раз.

  3. Чтобы добавить несколько действий для одного и того же триггера, нажмите «Добавить действие».

    Действия для одного и того же триггера выполняются в последовательном порядке.

  4. Перетащите действия, сгруппированные под триггером, чтобы изменить порядок.
  5. Чтобы изменить эти настроенные параметры позже, нажмите кнопку «Параметры» действия.
  6. Чтобы изменить действие или цель после добавления действия, удалите его и добавьте новый.

    При удалении триггера также удаляются все добавленные к нему действия.

Настроить действия с данными

Действия с данными настраиваются в настройках исходных виджетов, за исключением экспорта, который настраивается для источника данных на панели «Данные».Действия с данными включены по умолчанию, а кнопка Действия виджета автоматически включает все доступные действия с данными.

  1. На панели «Действия» на вкладке «Действие с данными» снимите или отметьте флажком действия с данными, чтобы указать параметры для меню действий виджета. В противном случае отключите параметр «Включить действие с данными», чтобы исключить кнопку «Действия» из виджета.
  2. Для каждого действия с проверенными данными выберите целевые виджеты, с которыми будет взаимодействовать исходный виджет, например, виджет «Карта».

    Вы можете выбрать только целевые виджеты, которые находятся на той же странице, что и исходный виджет.

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

    Совет:

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

  3. Чтобы исключить действие «Экспорт данных», перейдите на панель «Данные» и отключите параметр Разрешить экспорт для источника данных.

Пример использования

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

  • Вы хотите создать приложение, которое сравнивает две карты, две сцены или карту и сцену, так чтобы экстенты двух Виджеты карты всегда должны быть синхронизированы.Вы можете добавить триггер Extent Changes к обоим виджетам, выбрать другой виджет в качестве цели для каждого и выбрать действие Pan to или Zoom to.
  • Вы добавляете виджет «Список» рядом с виджетом «Карта» и хотите, чтобы в списке отображались только записи в текущем экстенте карты. Вы можете добавить триггер «Изменения экстента» в виджет «Карта», выбрать действие «Фильтровать записи данных» платформы, выбрать данные действия в качестве представления данных, настроенного в настройках виджета «Список», и настроить действие с пространственной взаимосвязью с экстентом.

Используйте действия с данными для поддержки требований к дизайну приложения, например: