Инструменты пользователя

Инструменты сайта


products:pyrog:tutorials:dev:main

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
products:pyrog:tutorials:dev:main [2026/06/05 00:54] ironmeshproducts:pyrog:tutorials:dev:main [2026/07/25 18:47] (текущий) – [2.3.2 Процедура перевода] ironmesh
Строка 1: Строка 1:
 ====== Руководство по разработке ====== ====== Руководство по разработке ======
  
-Для разработки собственного приложения нужно иметь минимум базовые навыки разработки на Python и PySide6.+Для разработки собственного приложения нужно иметь минимум базовые навыки разработки на Python и PySide6. Если возникли вопросы, присоединяйтесь к [[https://discord.gg/A7AfhnSacA|💬Форуму]]
  
-==== 2.Скачивание исходников и подготовка IDE к работе ====+====1. Быстрый старт =====
  
-Скачайте исходные файлы программы из [[https://github.com/iron-mesh/pyrog|этого репозитория]]. Установите Python, если по какой-то причине он еще не установленили обновите, нам нужна версия 3.13 или новее. Распакуйте архив и запустите файл ''...Pyrog/manager/Pyrog.pyw'', если PySide6 не установлен запустится утилита для автоматической установки, просто дайте согласие на установку и он скачается установится в систему. Если все прошло успешно, то на экране появится окно Менеджера, так далее по тексту будет называться программа для управления плагинами, теми самыми мини-приложениями, о которых ранее шла речь. Не буду подробно расписывать как его использовать, так как с этим легко разобраться самостоятельно, в крайнем случае можете обратиться к [[https://github.com/iron-mesh/pyrog/blob/master/help/manager-manual.md|данному руководству]].+==== 1.1 Скачивание исходников и подготовка IDE к работе ====
  
-Теперь, нам нужно подготовить среду разработки для работы, я опишу процесс в контексте IDE PyCharm, но вы же можете использовать другой редактор. В папке ''...Pyrog/manager/plugins'' есть папка с именем Template, скопируйте ее и переименуйте, так как данное имя в любом регистре зарезервировано и будет проигнорировано. Затем создайте откройте папку как проект, и зайдите в настройки ''Settings -> Project Structure'' нажмите на ''Add content Root'' и выберите папку Pyrog и установите ее в качестве ''Source''. Теперь, запустите Pyrog.pyw и увидите интерфейс шаблона.+Скачайте исходные файлы программы из [[https://github.com/iron-mesh/pyrog|этого репозитория]]. Установите [[https://Python.org|Python]], если по какой-то причине он еще не установленили обновите, нам нужна версия 3.13 или новее. Распакуйте архив и запустите файл ''...Pyrog/manager/Pyrog.pyw''если PySide6 не установлен запустится утилита для автоматической установкипросто дайте согласие на установку, он скачается и установится в систему. Если все прошло успешно, то на экране появится окно Менеджера, так далее по тексту будет называться программа для управления плагинами (мини-приложениями). Не буду подробно расписывать как его использовать, так как с этим легко разобраться самостоятельно, в крайнем случае можете обратиться к [[..:usermanual:main|данному руководству]].
  
-Можете выключить ненужные плагинычтобы не мешали.+Теперь, нам нужно подготовить среду разработки для работы, я опишу процесс в контексте IDE PyCharm, но вы же можете использовать любой другой редактор. В папке ''...Pyrog/manager/plugins'' есть папка с именем //Template//, скопируйте ее и переименуйте, так как данное имя в любом регистре зарезервировано и будет проигнорировано. Затем откройте папку как проекти зайдите в настройки ''Settings -> Project Structure'' нажмите на ''Add content Root'' и выберите папку ''Pyrog'' и установите ее в качестве ''Source''. Теперь, запустите скрипт **Pyrog.pyw** и увидите интерфейс шаблона.
  
-==== 2.2 Анатомия плагина ====+ожете выключить ненужные плагины, чтобы не мешали.
  
-В Python пакетом является папка, которая содержит в себе файл с именем ''__init__.py'', плагин тот же самый пакет, который Менеджер импортирует, когда пользователь к нему обращается. Плагин загружается в случаях, когда нужно отобразить интерфейс или пользователь обратился к настройкам плагина, это сделано для оптимизации, "зачем загружать то, что возможно не будет использовано". Ниже приводится структура папки плагина "в максимальной комплектации".+==== 1.2 Анатомия плагина ==== 
 + 
 +В Python пакетом является папка, которая содержит в себе файл с именем  ''____init____.py'', плагин тот же самый пакет, который Менеджер импортирует, когда пользователь к нему обращается. Плагин загружается в случаях, когда нужно отобразить интерфейс или пользователь обратился к настройкам плагина, это сделано для оптимизации, "зачем загружать то, что возможно не будет использовано". Ниже приводится структура папки плагина "в максимальной комплектации".
  
 <code -> <code ->
Строка 28: Строка 30:
 </code> </code>
  
-В файле ''__init__.py'' должен находится атрибут с именем ''plugin'', который является ссылкой на класс плагина, данный класс может быть объявлен в самом файле или в другом, который затем будет импортирован. В принципе, можно все приложение написать в файле ''__init__.py'', но лично я бы так делать не стал, а вот сам класс - вполне. В шаблоне только одна строчка кода, где из модуля plugin импортируется класс MyPlugin под псевдонимом plugin.+В файле ''__init__.py'' должен находится атрибут с именем ''plugin'', который является ссылкой на класс плагина, данный класс может быть объявлен в самом файле или в любом другом, который затем будет импортирован. В принципе, можно все приложение написать в файле ''____init____.py'', но лично я бы так делать не стал, а вот сам класс - вполне. В шаблоне только одна строчка кода, где из модуля plugin импортируется класс **MyPlugin **под псевдонимом ''plugin''.
  
 <code python> <code python>
Строка 41: Строка 43:
 </code> </code>
  
-В файле manifest.json хранится информация о плагине, наличие этого файла желательно, но не является обязательным, просто некоторая функциональность будет ограничена. Пример содержания файла+В файле ''manifest.json'' хранится информация о плагине, наличие этого файла не является обязательным, просто некоторая функциональность будет ограничена. Пример содержания файла
  
 <code json> <code json>
Строка 69: Строка 71:
 </code> </code>
  
-Пояснение к атрибутам:+Пояснения к атрибутам:
  
-  * **name** имя плагина, старайтесь делать его простым и лаконичным, если отсутствует или не валидно, то будет использовано имя пакета; +^ Поле ^ Описание ^ Пример ^ 
-  **description** короткое описание плагина; +**name** имя плагина, старайтесь делать его простым и лаконичным, если отсутствует или не валидно, то будет использовано имя пакета | "My cool Plugin" | 
-  **project_page_url** ссылка на страницу проекта в интернете; +**description** короткое описание плагина | "The plugin does..." | 
-  **manual_url** ссылка на страницу с документацией в интернете; +**project_page_url** ссылка на страницу проекта в интернете | "[[https://mysite.org/my-cool-plugin]]" | 
-  **version** текущая версия плагина, обозначение может иметь только числовые обозначения, разделенные точкой, от 1 до 3 цифр ("1", "1.0", "2.34.25"); +**manual_url** ссылка на страницу с документацией в интернете | "[[https://docs.mysite.org/my-cool-plugin]]" | 
-  **version_status** статус версии, любое строковое значение, но обычно это обозначение готовности релиза (alpha, beta, pre-release); +**version** текущая версия плагина, обозначение может иметь только числовые обозначения, разделенные точкой, от 1 до 3 цифр ("1", "1.0", "2.34.25"| "1.0.0" | 
-  **init_release_date** первый релиз плагина, дата в формате ISO8601 YYYY-MM-DD; +**version_status** статус версии, любое строковое значение, но обычно это обозначение готовности релиза (alpha, beta, pre-release) | "beta" | 
-  **update_date** дата релиза текущей версии, формат как у init_release_date +**init_release_date** первый релиз плагина, дата в формате ISO8601 YYYY-MM-DD | "2025-01-01" | 
-  * **developer** имя разработчика ; +**update_date** дата релиза текущей версии, формат как у **init_release_date** | "2026-01-01"
-  **developer_email** электронная почта разработчика; +**developer** имя разработчика | "IronMesh" | 
-  **developer_webpage** веб-страница разработчика; +**developer_email** электронная почта разработчика | "mail@mail.com" | 
-  **repository_url** ссылка на git репозиторий плагина; +**developer_webpage** веб-страница разработчика | "[[https://mysite.org]]" | 
-  **forum_url** ссылка на форум; +**repository_url** ссылка на git репозиторий плагина | "[[https://github.com/iron-mesh/pyrog]]" | 
-  **dependencies** набор пакетов, которые нужны для запуска плагина, и которые могут быть загружены из хранилища PyPI; представляет из себя список строк формата: ''<имя пакета для импорта>,<имя пакета для загрузки утилитой pip>'', если имя пакета для импорта совпадает с именем пакета для загрузки, то вторую часть можно упустить, в таком случае запятую ставить не нужно; +**forum_url** ссылка на форум | "[[https://myforum.org]]" | 
-  **source_language** локаль оригинального языка, код языка в формате ISO639-1и код страны по стандарту ISO 3166-2, например, ''en_US'', если данное поле отсутствует или некорректно, то механизм интернационализации задействован не будет.+**dependencies** набор пакетов, которые нужны для запуска плагина, и которые могут быть загружены из хранилища PyPI; представляет из себя список строк формата: ''<имя пакета для импорта>,<имя пакета для загрузки утилитой pip>'', если имя пакета для импорта совпадает с именем пакета для загрузки, то вторую часть можно упустить, в таком случае запятую ставить не нужно | ["bs4,beautifulsoup4", "PIL,Pillow",         "cv2,opencv-python",         "PySide6"] | 
 +**source_language** локаль оригинального языка, код языка в формате ISO639-1 и код страны согласно стандарту ISO 3166-2,  если данное поле отсутствует или некорректно, то механизм интернационализации задействован не будет | "en_US" |
  
 Папка ''translations'' содержит в себе словари переводов для локализации интерфейса, если такая опция не требуется, то ее добавлять не нужно. Внутри данной папки содержатся папки со словарями в формате ''.QM'', имена папок выбираются в соответствии с кодом языка и территории, аналогично тому как выбирается значения для поля ''source_language'' в файле манифеста. Папка ''translations'' содержит в себе словари переводов для локализации интерфейса, если такая опция не требуется, то ее добавлять не нужно. Внутри данной папки содержатся папки со словарями в формате ''.QM'', имена папок выбираются в соответствии с кодом языка и территории, аналогично тому как выбирается значения для поля ''source_language'' в файле манифеста.
Строка 159: Строка 162:
 | BoolProperty | bool | Булевое значение | | BoolProperty | bool | Булевое значение |
 | ColorProperty | str | Строка с кодом цвета в формате HEX, например, ''#ffbbcc'' | | ColorProperty | str | Строка с кодом цвета в формате HEX, например, ''#ffbbcc'' |
-| FontProperty |   | Кортеж с данными о шрифте (имя шрифта, стиль, размер) |+| FontProperty | tuple[str, str, int]  | Кортеж с данными о шрифте (имя шрифта, стиль, размер) |
 | ComboBoxProperty | int | Индекс выбранного элемента списка | | ComboBoxProperty | int | Индекс выбранного элемента списка |
 | StringProperty | str | Строка | | StringProperty | str | Строка |
Строка 166: Строка 169:
 | FilePathProperty | str | Строка, в которой содержится абсолютный путь к файлу или папке | | FilePathProperty | str | Строка, в которой содержится абсолютный путь к файлу или папке |
 | FilePathListProperty | tuple[tuple[str, str], ...] | Кортеж с абсолютными путями к файлам или папкам, плюс их псевдонимы | | FilePathListProperty | tuple[tuple[str, str], ...] | Кортеж с абсолютными путями к файлам или папкам, плюс их псевдонимы |
 +| PlainTextProperty | str | Строка, отличается от StringListProperty тем, что предоставляет многострочное поле ввода |
 +| DateTimeProperty | str | Дата и время в виде строки, в формате: ''2026-12-25_01:25:16 (<год>-<месяц>-<числов>_<час>:<минута>:<секунда>)'' |
  
-Чтобы использовать свойства в коде, просто импортируем класс Settings, и вызываем свойство ''value'' для нужного Свойства, например, ''Settings.int_property.value''+Чтобы использовать свойства в коде, просто импортируем класс **Settings**, и вызываем свойство ''value'' для нужного Свойства, например, ''Settings.int_property.value''
  
 Итак, вы познакомились с устройством плагина и этих минимальных сведений достаточно для разработки своих программ, вы можете построить приложение любой сложности, Pyrog не накладывает никаких ограничений. Итак, вы познакомились с устройством плагина и этих минимальных сведений достаточно для разработки своих программ, вы можете построить приложение любой сложности, Pyrog не накладывает никаких ограничений.
Строка 173: Строка 178:
 Как вы видите для того чтобы работать с Pyrog нужно объявить специальный класс плагина, который возвращает ссылку на интерфейс плагина и опционально может предоставлять ссылку на контейнер свойств (PropertyContainer) с пользовательскими настройками, и при этом не нужно заботиться о сохранении данных, система все сделает сама. Но, не бросайте чтение, далее я раскрою многие механики и нюансы системы. Как вы видите для того чтобы работать с Pyrog нужно объявить специальный класс плагина, который возвращает ссылку на интерфейс плагина и опционально может предоставлять ссылку на контейнер свойств (PropertyContainer) с пользовательскими настройками, и при этом не нужно заботиться о сохранении данных, система все сделает сама. Но, не бросайте чтение, далее я раскрою многие механики и нюансы системы.
  
-===== 3. Расширяем познания =====+===== 2. Расширяем познания =====
  
 В предыдущем разделе мы познакомились с основами разработки плагинов, далее я расскажу о: В предыдущем разделе мы познакомились с основами разработки плагинов, далее я расскажу о:
Строка 186: Строка 191:
   * других нюансах и особенностях.   * других нюансах и особенностях.
  
-==== 3.1 Жизненный цикл плагина ====+==== 2.1 Жизненный цикл плагина ====
  
 Когда пользователь запускает Менеджер, то пакет Плагина импортируется не сразу, а при определенных условиях. Когда пользователь переходит во вкладку плагина, или ранее была установлена опция инициализации на старте Менеджера, или он запрашивает пользовательские настройки, то в данных случаях запускается процедура импорта. Менеджер импортирует пакет и ищет класс Плагина, в зависимости от требований запускается определенный метод: Когда пользователь запускает Менеджер, то пакет Плагина импортируется не сразу, а при определенных условиях. Когда пользователь переходит во вкладку плагина, или ранее была установлена опция инициализации на старте Менеджера, или он запрашивает пользовательские настройки, то в данных случаях запускается процедура импорта. Менеджер импортирует пакет и ищет класс Плагина, в зависимости от требований запускается определенный метод:
Строка 197: Строка 202:
 Если пользователь деактивирует плагин, то все ранее загруженные модули пакета будут удалены из памяти. Если пользователь деактивирует плагин, то все ранее загруженные модули пакета будут удалены из памяти.
  
-==== 3.2 Ваш универсальный помощник ====+==== 2.2 Ваш универсальный помощник ====
  
 Для общения с Менеджером используйте класс ''Helper'' из ''PyUB.Types''. Вызывайте его конструктор в любом месте вашего кода, в любом случае каждый Плагин может иметь только один экземпляр "Помощника". Для общения с Менеджером используйте класс ''Helper'' из ''PyUB.Types''. Вызывайте его конструктор в любом месте вашего кода, в любом случае каждый Плагин может иметь только один экземпляр "Помощника".
Строка 203: Строка 208:
 Данный класс предоставляет следующие методы: Данный класс предоставляет следующие методы:
  
-Метод ^ Выполняемые действия ^ +=== 2.2.1 Методы ===
-| save_settings_parameters | Сохраняет параметры Свойств, находящиеся в контейнере, который получен через метод ''Plugin.settings()''+
-| save_settings_values | Сохраняет значения Свойств, находящиеся в контейнере, который получен через метод ''Plugin.settings()''+
-| plugin_dir_abspath | Возвращает строку с абсолютным путем к папке плагина | +
-| plugin_localstorage_dir_abspath | Возвращает строку с абсолютным путем к индивидуальной папке в локальном хранилище Менеджера (''...\Pyrog\manager\data\plugins_ls\<имя индивидуальной папки>'') |+
  
 +**save_settings_parameters()**
 +
 +Сохраняет параметры Свойств, находящиеся в контейнере, который получен через метод ''Plugin.settings()''
 +
 +**save_settings_values()**
 +
 +Сохраняет значения Свойств, находящиеся в контейнере, который получен через метод ''Plugin.settings()''
 +
 +**plugin_dir_abspath()**
 + 
 +Возвращает строку с абсолютным путем к папке плагина
 +
 +**plugin_localstorage_dir_abspath()**
 +
 +Возвращает строку с абсолютным путем к индивидуальной папке в локальном хранилище Менеджера (''...\Pyrog\manager\data\plugins_ls\<имя индивидуальной папки>'')
 +
 +
 +=== 2.2.2 Сигналы ===
 Helper также имеет ряд сигналов, сообщающих о действиях пользователя Helper также имеет ряд сигналов, сообщающих о действиях пользователя
  
Строка 216: Строка 235:
 | app_closing | Пользователь закрыл Менеджер. Отправляется до того, как программа завершит работу. Полезен для безопасного завершения сессии. | | app_closing | Пользователь закрыл Менеджер. Отправляется до того, как программа завершит работу. Полезен для безопасного завершения сессии. |
 | settings_editing_starting | Пользователь открыл пользовательские настройки. Отправляется перед тем как окно редактирования будет выведено на экран. Полезно когда нужно подготовить программу к изменению настроек. | | settings_editing_starting | Пользователь открыл пользовательские настройки. Отправляется перед тем как окно редактирования будет выведено на экран. Полезно когда нужно подготовить программу к изменению настроек. |
-| settings_editing_finished | Пользователь завершил редактирование настроек. \\ Возвращает булевое значение, ''True'' - пользователь выбрал сохранить настройки, ''False'' - отказался сохранять настройки, если значения Свойств были изменены, то произойдет их откат до состояния перед началом редактирования. +| settings_editing_finished | Пользователь завершил редактирование настроек. \\ Возвращает булевое значение, ''True'' - пользователь выбрал сохранить настройки, ''False'' - отказался сохранять настройки, если значения Свойств были изменены, то произойдет их откат до состояния перед началом редактирования. В любом случае вы можете отслеживать изменения значений Свойств на лету. |
- +
-В любом случае вы можете отслеживать изменения значений Свойств на лету.+
  
 Весь код в Менеджере выполняется синхронно, это значит исполнение не продолжится пока не выполнится код реакции на сигналы. Напомню, что обработчики сигналам подключаются так: ''helper.<сигнал>.connect(<обработчик>)''. Весь код в Менеджере выполняется синхронно, это значит исполнение не продолжится пока не выполнится код реакции на сигналы. Напомню, что обработчики сигналам подключаются так: ''helper.<сигнал>.connect(<обработчик>)''.
  
-==== 3.3 Делаем локализацию интерфейса ====+==== 2.3 Делаем локализацию интерфейса ====
  
 Итак, для разблокировки механизма интернационализации нужно в файле манифеста указать исходный язык, например, ''"source_language": "en_GB"'', таким образом даем системе понять какой язык является оригинальным. Далее в корневой папке плагина создаем директорию ''translations'', а внутри папки для словарей, которые должны иметь имена, состоящее из кода языка и территории. Обратитесь к предыдущему разделу, где мы заполняли файл манифеста. Таким образом папки могут иметь следующие имена: ''en_US'', ''es_ES'', ''ru_RU'', ''de_DE''. Из этих папок будут загружаться словари переводов в формате ''.QM'', вложенные папки будут игнорироваться. Итак, для разблокировки механизма интернационализации нужно в файле манифеста указать исходный язык, например, ''"source_language": "en_GB"'', таким образом даем системе понять какой язык является оригинальным. Далее в корневой папке плагина создаем директорию ''translations'', а внутри папки для словарей, которые должны иметь имена, состоящее из кода языка и территории. Обратитесь к предыдущему разделу, где мы заполняли файл манифеста. Таким образом папки могут иметь следующие имена: ''en_US'', ''es_ES'', ''ru_RU'', ''de_DE''. Из этих папок будут загружаться словари переводов в формате ''.QM'', вложенные папки будут игнорироваться.
  
-==== 3.3.1 Языковые константы ====+==== 2.3.1 Языковые константы ====
  
 Языковые константы (ЯК) - это объекты, которые хранят в себе оригинальный текст и текст перевода, добавлены для того чтобы избежать конфликтов при использовании стандартного механизма локализации, когда перевод извлекается из загруженных словарей, например, в случае когда разные плагины используют разные языки и словари для них загружены одновременно, то если оригинальный текст перевода и контекст будут совпадать, то будет получен перевод из словаря, который был загружен последним. Итак, разберемся как с ними работать. Языковые константы (ЯК) - это объекты, которые хранят в себе оригинальный текст и текст перевода, добавлены для того чтобы избежать конфликтов при использовании стандартного механизма локализации, когда перевод извлекается из загруженных словарей, например, в случае когда разные плагины используют разные языки и словари для них загружены одновременно, то если оригинальный текст перевода и контекст будут совпадать, то будет получен перевод из словаря, который был загружен последним. Итак, разберемся как с ними работать.
Строка 245: Строка 262:
   * контекст   * контекст
   * текст константы   * текст константы
-  * (опционально) строка идентификатор, когда один и тот же исходный текст используется в одном контексте, но в разных ролях. Параметры аналогичны тем, что передаются функции PySide6.QtCore.QCoreApplication.translate(), кроме аргумента n.+  * (опционально) строка идентификатор, когда один и тот же исходный текст используется в одном контексте, но в разных ролях. Параметры аналогичны тем, что передаются функции ''PySide6.QtCore.QCoreApplication.translate()'', кроме аргумента n.
  
 Извлечь перевод (при его наличии, если он отсутствует, то используется оригинальный текст) можно несколькими способами. Импортируем ранее созданный модуль ''tranlslations.py'' Извлечь перевод (при его наличии, если он отсутствует, то используется оригинальный текст) можно несколькими способами. Импортируем ранее созданный модуль ''tranlslations.py''
Строка 310: Строка 327:
 Обращаю ваше внимание, что ЯК не производят форматирование строки, как это сделано в стандартной системе Qt, где число подставляется на место метки ''%n'', форматирование нужно будет реализовать самостоятельно. Языковые константы полезны там, где интерфейс создается динамически, их можно не использовать в главном виджете плагина, так как он живет в течение всей сессии, там вы можете использовать стандартную функцию ''PySide6.QtCore.QCoreApplication.translate()'' Обращаю ваше внимание, что ЯК не производят форматирование строки, как это сделано в стандартной системе Qt, где число подставляется на место метки ''%n'', форматирование нужно будет реализовать самостоятельно. Языковые константы полезны там, где интерфейс создается динамически, их можно не использовать в главном виджете плагина, так как он живет в течение всей сессии, там вы можете использовать стандартную функцию ''PySide6.QtCore.QCoreApplication.translate()''
  
-==== 3.3.2 Процедура перевода ====+==== 2.3.2 Процедура перевода ====
  
 Теперь, пришло время поговорить о том, как обновить переводы в константах и там где он был размечен стандартными средствами Qt. Когда пользователь меняет язык, то класс Helper отправляет сигнал ''plugin_language_changing'' с кодом языка, задача разработчика реализовать процедуру перевода. Теперь, пришло время поговорить о том, как обновить переводы в константах и там где он был размечен стандартными средствами Qt. Когда пользователь меняет язык, то класс Helper отправляет сигнал ''plugin_language_changing'' с кодом языка, задача разработчика реализовать процедуру перевода.
  
 Рассмотрим подробнее, что происходит в системе по шагам: Рассмотрим подробнее, что происходит в системе по шагам:
- +  - Пользователь изменил язык 
-  1. Пользователь изменил язык +  Менеджер загружает доступные словари для выбранного языка 
-  2. Менеджер загружает доступные словари для выбранного языка +  Менеджер через Helper отправляет сигнал plugin_language_changing плагину о том, что язык изменился и ему нужно провести необходимые процедуры 
-  3. Менеджер через Helper отправляет сигнал plugin_language_changing плагину о том, что язык изменился и ему нужно провести необходимые процедуры +  Если плагин привязал обработчики к сигналу, то производится их выполнение 
-  4. Если плагин привязал обработчики к сигналу, то производится их выполнение +  Менеджер выгружает ранее установленные словари
-  5. Менеджер выгружает ранее установленные словари+
  
 Итак, нам как разработчикам плагинов нужно сосредоточиться на шаге 4, когда у нас есть окно возможностей между загрузкой и выгрузкой словарей, для этого посмотрим на то, как это реализовано во встроенном плагине TS generator, который можно найти в папке ''...Pyrog\manager\plugins\translator'', в пакете ''view'' в модуле ''main_widget.py'' определен метод, который отрабатывает данную процедуру (''MainWidget._on_retranslate()''), посмотрим на него поближе Итак, нам как разработчикам плагинов нужно сосредоточиться на шаге 4, когда у нас есть окно возможностей между загрузкой и выгрузкой словарей, для этого посмотрим на то, как это реализовано во встроенном плагине TS generator, который можно найти в папке ''...Pyrog\manager\plugins\translator'', в пакете ''view'' в модуле ''main_widget.py'' определен метод, который отрабатывает данную процедуру (''MainWidget._on_retranslate()''), посмотрим на него поближе
Строка 355: Строка 371:
 Когда мы используем QtDesigner для создания форм интерфейса, то в коде формы, которую он генерирует есть метод ''retranslateUi()'', можно подключить его к сигналу ''plugin_language_changing'' или вызвать его в другом обработчике данного сигнала. Когда мы используем QtDesigner для создания форм интерфейса, то в коде формы, которую он генерирует есть метод ''retranslateUi()'', можно подключить его к сигналу ''plugin_language_changing'' или вызвать его в другом обработчике данного сигнала.
  
-==== 3.3.3 Подготовка словарей ====+==== 2.3.3 Подготовка словарей ====
  
 После того как работа над кодом плагина закончена, можно приступить к локализации. Исходники нужно преобразовать в TS файлы, а затем перевести исходные тексты на нужный язык и скомпилировать их в QM словари. Несколько лет назад я уже писал статью на эту тему, хотя она актуальна для PySide2, но в целом принцип не изменился. После того как работа над кодом плагина закончена, можно приступить к локализации. Исходники нужно преобразовать в TS файлы, а затем перевести исходные тексты на нужный язык и скомпилировать их в QM словари. Несколько лет назад я уже писал статью на эту тему, хотя она актуальна для PySide2, но в целом принцип не изменился.
Строка 377: Строка 393:
 Сейчас можно не делать перевод полностью руками, а отдать TS-файл нейросети и дать ей команду сделать переводы на нужные языки, они сами "знают" разметку файлов и никак ее не ломают. Я пробовал сделать это с помощью Claude и Deepseek. Просто передал им файл и задал команду: "Это ts файл pyside6 исходный язык английский, там уже есть перевод на русский. Замени русский перевод на французский". После останется только проверить перевод и скомпилировать словарь. Сейчас можно не делать перевод полностью руками, а отдать TS-файл нейросети и дать ей команду сделать переводы на нужные языки, они сами "знают" разметку файлов и никак ее не ломают. Я пробовал сделать это с помощью Claude и Deepseek. Просто передал им файл и задал команду: "Это ts файл pyside6 исходный язык английский, там уже есть перевод на русский. Замени русский перевод на французский". После останется только проверить перевод и скомпилировать словарь.
  
-==== 3.4 Система свойств ====+==== 2.4 Система свойств ====
  
-==== 3.4.1 Как работать со Свойствами ====+==== 2.4.1 Как работать со Свойствами ====
  
 Свойство в контексте Pyrog это программная единица, которая хранит в себе данные определенного типа; имеет набор параметров, которые определяют его поведение; и графический интерфейс для того чтобы пользователь мог манипулировать хранящимся значением. Свойство в контексте Pyrog это программная единица, которая хранит в себе данные определенного типа; имеет набор параметров, которые определяют его поведение; и графический интерфейс для того чтобы пользователь мог манипулировать хранящимся значением.
Строка 413: Строка 429:
 **Как получить доступ к графическому интерфейсу** Каждое Свойство имеет метод ''get_input_widget()'', который возвращает виджет для редактирования значения, его вы может использовать для встраивания в свой интерфейс. **Как получить доступ к графическому интерфейсу** Каждое Свойство имеет метод ''get_input_widget()'', который возвращает виджет для редактирования значения, его вы может использовать для встраивания в свой интерфейс.
  
-==== 3.4.2 Как работать с контейнером свойств ====+==== 2.4.2 Как работать с контейнером свойств ====
  
 По своей сути Контейнер свойств это обычный класс, унаследованный от ''PropertyContainer'', внутри которого объявляются атрибуты, которым присваиваются экземпляры Свойств, только не изменяйте встроенные атрибуты, чтобы не нарушить работу программы. Контейнер свойств нужен для удобной манипуляции Cвойствами и выполняет следующую работу: По своей сути Контейнер свойств это обычный класс, унаследованный от ''PropertyContainer'', внутри которого объявляются атрибуты, которым присваиваются экземпляры Свойств, только не изменяйте встроенные атрибуты, чтобы не нарушить работу программы. Контейнер свойств нужен для удобной манипуляции Cвойствами и выполняет следующую работу:
Строка 481: Строка 497:
   * ''pc_set_prop_params_from_dict()'' - принимает словарь в том же формате, что возвращает метод ''pc_prop_params_as_dict()'' и производит обновление параметров Свойств, производится проверка на соответствие типу Свойства, если он не идентичен, то обновления не происходит.   * ''pc_set_prop_params_from_dict()'' - принимает словарь в том же формате, что возвращает метод ''pc_prop_params_as_dict()'' и производит обновление параметров Свойств, производится проверка на соответствие типу Свойства, если он не идентичен, то обновления не происходит.
  
-==== 3.4.3 Как разработать собственное Свойство ====+==== 2.4.3 Как разработать собственное Свойство ====
  
 Может так случиться, что встроенных Свойств вам будет недостаточно, для таких случае я составил пошаговый гайд по разработке собственного Cвойства, и разбирать мы его будем на примере BoolListProperty, данное свойство задает кортеж значений типа ''bool''. Итак, приступим. Может так случиться, что встроенных Свойств вам будет недостаточно, для таких случае я составил пошаговый гайд по разработке собственного Cвойства, и разбирать мы его будем на примере BoolListProperty, данное свойство задает кортеж значений типа ''bool''. Итак, приступим.
  
-**Шаг 1** Объявляем класс и создаем сигнал. Имя сигнала не изменять!+**Шаг 1.** Объявляем класс и создаем сигнал. Имя сигнала не изменять!
  
 <code python> <code python>
Строка 492: Строка 508:
 </code> </code>
  
-**Шаг 2** Задание схемы параметров Свойства, сохраняется в атрибуте ''_param_schema''+**Шаг 2.** Задание схемы параметров Свойства, сохраняется в атрибуте ''_param_schema''
  
 <code python> <code python>
Строка 544: Строка 560:
 параметр ''default_value'' должен принимать кортеж со значениями типа ''bool'', аргумент self принимает его значение, ''args'' - в данному случае список с одним элементом - значением параметра ''_items''; далее производится проверка, чтобы длины кортежей были равными и чтобы все значения кортежа были типа ''bool''. параметр ''default_value'' должен принимать кортеж со значениями типа ''bool'', аргумент self принимает его значение, ''args'' - в данному случае список с одним элементом - значением параметра ''_items''; далее производится проверка, чтобы длины кортежей были равными и чтобы все значения кортежа были типа ''bool''.
  
-**Шаг 3** Создаем свойства для всех параметров, используя функцию ''create_param_property()'' (PyUB.Type.Properties.utils). В качестве аргумента указываем строку с именем параметра, определенного в ''_param_schema'', функция создает приватный атрибут в классе, идентичный имени ключа.+**Шаг 3.** Создаем свойства для всех параметров, используя функцию ''create_param_property()'' (PyUB.Type.Properties.utils). В качестве аргумента указываем строку с именем параметра, определенного в ''_param_schema'', функция создает приватный атрибут в классе, идентичный имени ключа.
  
 <code python> <code python>
Строка 565: Строка 581:
   * ''doc'' - строка документации   * ''doc'' - строка документации
  
-**Шаг 4** Пишем конструктор. В модуле ''tr'' находятся служебные языковые константы, перед использованием надо импортировать+**Шаг 4.** Пишем конструктор. В модуле ''tr'' находятся служебные языковые константы, перед использованием надо импортировать
  
 <code python> <code python>
Строка 588: Строка 604:
 Набор аргументов должен совпадать с набором параметров. Набор аргументов должен совпадать с набором параметров.
  
-**Шаг 5** Пишем метод создания виджета ввода+**Шаг 5.** Пишем метод создания виджета ввода
  
 <code python> <code python>
Строка 607: Строка 623:
 На первом шаге проверяем был ли ранее создан виджет, если был, то возвращаем его. Обратите внимание, сам экземпляр виджета должен сохранятся в приватном атрибуте ''_widget''. Затем оборачиваем его в Resetter, этот класс-обертка нужен для создания рядом с виджетом кнопки для сброса значения. Затем устанавливаем политику размера виджета, вложенный виджет доступен через свойство ''child_widget'' у Resetter. Когда пользователь вызывает сброс значения, то Resetter отправляет сигнал ''reset_requested'', и мы привязываем метод ''reset_value()'' самого свойства в качестве обработчика. Затем сигналу ''itemChanged'' виджета подключаем обработчик ''_on_widget_value_changed'' и в конце вызываем методы для обновления параметров и значения виджета. На первом шаге проверяем был ли ранее создан виджет, если был, то возвращаем его. Обратите внимание, сам экземпляр виджета должен сохранятся в приватном атрибуте ''_widget''. Затем оборачиваем его в Resetter, этот класс-обертка нужен для создания рядом с виджетом кнопки для сброса значения. Затем устанавливаем политику размера виджета, вложенный виджет доступен через свойство ''child_widget'' у Resetter. Когда пользователь вызывает сброс значения, то Resetter отправляет сигнал ''reset_requested'', и мы привязываем метод ''reset_value()'' самого свойства в качестве обработчика. Затем сигналу ''itemChanged'' виджета подключаем обработчик ''_on_widget_value_changed'' и в конце вызываем методы для обновления параметров и значения виджета.
  
-**Шаг 6** Пишем методы обновления параметров и значения виджета+**Шаг 6.** Пишем методы обновления параметров и значения виджета
  
 <code python> <code python>
Строка 646: Строка 662:
 Используем функцию ''get_lang_const_translation()'' для извлечения перевода из языковой константы или выводит исходный текст. Используем функцию ''get_lang_const_translation()'' для извлечения перевода из языковой константы или выводит исходный текст.
  
-**Шаг 7** Пишем обработчик события: изменение значения в виджете+**Шаг 7.** Пишем обработчик события: изменение значения в виджете
  
 <code python> <code python>
Строка 668: Строка 684:
 Метод ''update_reset_btn_visibility()'' сам сравнит значение Свойства с дефолтным и учтет значение параметра ''show_reset_btn''. Метод ''update_reset_btn_visibility()'' сам сравнит значение Свойства с дефолтным и учтет значение параметра ''show_reset_btn''.
  
-**Шаг 8** Пишем метод валидации значения+**Шаг 8.** Пишем метод валидации значения
  
 <code python> <code python>
Строка 697: Строка 713:
 **Шаг 9 (ситуативный)** Иногда нужно переопределить код свойства value, например, как это было нужно для Свойства FloatProperty, так как код класса Property использует для сравнения значений операцию ''!='', что не корректно использовать с объектами типа ''float''. Таким образом, если тип данных Свойства не поддерживает сравнение на равенство, то код свойства ''value'' придется переопределить. **Шаг 9 (ситуативный)** Иногда нужно переопределить код свойства value, например, как это было нужно для Свойства FloatProperty, так как код класса Property использует для сравнения значений операцию ''!='', что не корректно использовать с объектами типа ''float''. Таким образом, если тип данных Свойства не поддерживает сравнение на равенство, то код свойства ''value'' придется переопределить.
  
-==== 3.4.4 Разработка кастомного интерфейса для контейнера свойств ====+==== 2.4.4 Разработка кастомного интерфейса для контейнера свойств ====
  
 Если вас не удовлетворяет стандартный интерфейс вывода виджетов Свойств, то ничего не мешает сделать свой вариант. Давайте сделаем интерфейс, где Свойства разбиты на категории, и каждая из них занимает отдельную вкладку. Ниже показана моя реализация. Если вас не удовлетворяет стандартный интерфейс вывода виджетов Свойств, то ничего не мешает сделать свой вариант. Давайте сделаем интерфейс, где Свойства разбиты на категории, и каждая из них занимает отдельную вкладку. Ниже показана моя реализация.
Строка 759: Строка 775:
 Тут все просто, генерируем список суффиксов для отбора Свойств; затем для каждого суффикса создаем виджет QWidget, вкладываем его в область прокрутки QScrollArea, чтобы длинные списки не вылезали за пределы экрана, и виджету задаем макет QFormLayout, который выводит виджеты в 2 столбца; далее отсеиваем свойства по имени с нужным суффиксом, для получения доступа к Свойствам используем метод ''pc_properties()'' - это функция-генератор, которая выводит все Свойства в контейнере, возвращает кортеж(''<имя атрибута>'', ''<ссылка на экземпляр Свойства>''); далее располагаем виджеты в макете и готовый виджет раздела добавляет в качестве вкладки экземпляру QTabWidget, в конце возвращаем его качестве результата метода. Тут все просто, генерируем список суффиксов для отбора Свойств; затем для каждого суффикса создаем виджет QWidget, вкладываем его в область прокрутки QScrollArea, чтобы длинные списки не вылезали за пределы экрана, и виджету задаем макет QFormLayout, который выводит виджеты в 2 столбца; далее отсеиваем свойства по имени с нужным суффиксом, для получения доступа к Свойствам используем метод ''pc_properties()'' - это функция-генератор, которая выводит все Свойства в контейнере, возвращает кортеж(''<имя атрибута>'', ''<ссылка на экземпляр Свойства>''); далее располагаем виджеты в макете и готовый виджет раздела добавляет в качестве вкладки экземпляру QTabWidget, в конце возвращаем его качестве результата метода.
  
-==== 3.5 Несколько слов о недостатках ====+==== 2.5 Несколько слов о недостатках ====
  
   * При импорте плагины выполняются не в изолированной среде, а фактически становятся частью всей программы и каждому из них доступен весь код в рамках сессии. Таким образом любой плагин может вмешаться в работу Менеджера и других плагинов, только такое возможно при намеренном вредительстве, это нужно учитывать при использовании сторонних плагинов. Так или иначе нарушить выполнение может и допущенная ошибка в коде, например, занять весь главный поток приложения, что повесит всю программу и потребует её перезапуска, поэтому рекомендуется выполнять ресурсоемкие операции в отдельных потоках. Решение проблемы с зависанием на данный момент рассматривается и возможно в скором времени он будет внедрено.   * При импорте плагины выполняются не в изолированной среде, а фактически становятся частью всей программы и каждому из них доступен весь код в рамках сессии. Таким образом любой плагин может вмешаться в работу Менеджера и других плагинов, только такое возможно при намеренном вредительстве, это нужно учитывать при использовании сторонних плагинов. Так или иначе нарушить выполнение может и допущенная ошибка в коде, например, занять весь главный поток приложения, что повесит всю программу и потребует её перезапуска, поэтому рекомендуется выполнять ресурсоемкие операции в отдельных потоках. Решение проблемы с зависанием на данный момент рассматривается и возможно в скором времени он будет внедрено.
products/pyrog/tutorials/dev/main.1780610049.txt.gz · Последнее изменение: ironmesh