Todo в Mac App Store, используя архитектуру Pyqt5 ModelView для создания простого приложения Todo

Архитектура модели QT S MVC-подобный интерфейс для отображения данных в представлениях

Заводящее приложение показано ниже.

Тодо 4+

В течение нескольких лет я использовал это приложение на своем iPad, на моем iPhone и моем MacBookPro. Если он продолжает прекрасно работать на iOS, кажется, что он не полностью следовал за эволюцией macOS.
Нет проблем с установкой. Но больше невозможно синхронизировать с Touodledo на macOS, в то время как он продолжает быть возможным на iOS: те же идентификаторы не распознаются.
Поэтому это становится ненужным для меня … и помощь Appigo, похоже, не спешит дать ответ на мой запрос. Всегда неприятно отметить, что концепция клиента теряет немного больше своей ценности каждый день.

Пиза д

С момента этого обновления я потерял все свои списки по номеру 12 и с которым я работаю ежедневно профессионально. Я управляю примерно 200 местами навсегда, это обновление включило наземные месяцы испытания ! Все задачи, организованные в списке, оказались сгруппированными в один глобальный список. Конечно, это не эта ошибка, которая заставит меня перейти к версии, говоря, что профессионал в этом программном обеспечении в остальном отлично. Я никогда не буду пленником. Мне остается только дождаться ответа поддержки, связанной с этим вечером по электронной почте, или затем найти старую версию этого великолепного программного обеспечения. Я действительно надеюсь, что это молодой пиуг новой версии, а не стратегия, которая заставит мигрировать в платную Pro версию, чтобы найти функциональные возможности, уже приобретенные с предыдущей версией. Для получения информации моя синхронизация сделана с Dropbox

Surlepoint, 13.12.2011

Синхро теперь никель

Я схватил перед синхронизацией, которая была медленной, плохо ходила и т. Д. Но теперь с iCloud, это мне подходит идеально.

Некоторые возможные улучшения: больше настроек отображения.
– Чтобы отобразить только то, что использует пользователь. Например, я не использую контексты или этикетки (в любом случае еще не), и я хотел бы больше не отображать эти варианты, которые «загрязняют» мой тодо. Я хотел бы иметь только «дата / приоритет / список / тип» для моих повседневных заметок и щелкните правой кнопкой мыши для вариантов, которые я редко использую
– И наоборот, я хотел бы простой дисплей PLSU, чем меню «Drop -Down», чтобы выбрать дату, приоритет, список, … если у меня есть только три списка, то будет достаточно трех кнопок, и я мог бы выбрать свой список Один щелчок вместо того, чтобы нажать на меню «Drop -Down», найдите имя и нажмите на него. (То же самое для приоритета, особенно то, что мы переходим от очень визуального рисунка в списке TODO к выбору имени в опциях, что не очень естественно.

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

Приложение конфиденциальности

Разработчик, Appigo, не предоставил подробностей о своей частной практике и обработке данных Apple. Для получения дополнительной информации см. Политику конфиденциальности разработки.

Архитектура модели
QT MVC-подобный интерфейс для отображения данных в представлениях

Когда вы начнете создавать более сложные приложения с Pyqt5, вы, вероятно, столкнетесь с синхронизацией, поддержав виджеты с вашими данными. Данные, хранящиеся в виджетах (e.г. Простой Qlistwidget) не доступен для манипулирования из Python – изменения требуют, чтобы вы получили элемент, получили данные, а затем установите его обратно. Решением по умолчанию является поддержание внешнего представления данных в Python, а затем EITH дубликат обновления как данных, так и виджет, или просто переписать виджет Wihole из данных. Это может стать уродливым быстро и приводит к большому количеству шаблонов только для того, чтобы возиться с данными.

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

Этот урок представляет ключевые аспекты архитектуры QT ModelView и использует его для создания простого настольного приложения TODO в PYQT5.

Модель контроллера представления

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

MVC Design Pattenn Fets три основных компонента –

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

Это QT приземляется, различие между представлением и контроллером становится немного мутным. QT принимает события ввода от пользователя (через ОС) и делегирует их в виджеты (контроллер) для обработки. Тем не менее, виджеты также обрабатывают представление текущего состояния пользователю, поставив их прямо в представление. Вместо того, чтобы агонизировать, где нарисовать линию, в QT-Speak представление и контроллер с объединенными модельными/видоконтроллером «Модель» для простоты.

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

Представление модели

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

Вы можете использовать любой «хранилище данных» для вашей модели, например, стандартный список Python или Dictionary или база данных (через E.г. SQLALCHEMY) – это подходит для вас.

Две части по сути ответственны за –

  1. Чай модель Хранит данные или ссылку на него и возвращает индивидуальные или диапазоны записей, а также связанные метаданные или отображать инструкции.
  2. Чай вид Запрашивает данные из модели и отображает то, что возвращается в виджет.

В документации проводится углубленное обсуждение архитектуры QT.

Полное руководство по упаковке Python Gui Applications с Pyinstaller.

Упаковка Python Applications с Pyinstaller

[[скидка.Discount_pc]]]% off для следующего [Discount.Продолжительность]] [Скидка.Описание]] с помощью кода [скидка.код купона]]

Покупательный паритет

Застройщики в [[стране]] получить [[дисконтирование.DISCOUNT_PC]]] ВЫКЛЮЧИТЕ НА КНИГИ И КУРСАХ С КОДЕМ [DISCOUNT.код купона]]

Простой вид модели – список TODO

Чтобы продемонстрировать, как использовать модели на практике, мы составим очень простую реализацию списка Todo настольного компьютера. Это будет состоять из QlistView для списка элементов, QlineEdit для ввода новых элементов и набора кнопок для добавления, удаления или отметки элементов, как сделано.

UI

Простой пользовательский интерфейс был уродливым создателем QT и спасенным как MainWindow.UI . Чай .Файл пользовательского интерфейса и все остальные части можно загрузить ниже.

Создание простого приложения Todo в QT Creator

Создание простого приложения Todo в QT Creator

Заводящее приложение показано ниже.

Бегущий графический интерфейс (еще ничего не работает)

Бегущий графический интерфейс (еще ничего не работает)

Виджеты, доступные в интерфейсе, мы дали идентификаторы, показанные в таблице ниже.

Обжёх Добрый Описание
Todview QLISTVIEW Список текущих Todos
Tododit QlineEdit Ввод текста для создания нового элемента TODO
addbutton Qpushbutton Создайте новый Todo, добавив его в список Todos
Deletebutton Qpushbutton Удалить текущий выбран TODO, удалив его из списка Todos
Полная пустяка Qpushbutton Отметьте текущий выбранный TODO как сделанный

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

Модель

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

Для этого примера мы отображаем результат в QlistView . Соответствующая базовая модель для этого – Qabstractlistmodel . Определение схемы для нашей модели показано ниже.

Класс TOMODODEL (QTCORE.Qabstractlistmodel): def __init __ (self, *args, todos = none, ** kwargs): супер (Todomodel, self).__init __ (*args, ** kwargs).todos = todos или [] def data (self, index, роль): если роль == qt.DisplayRole: # см. Ниже для структуры данных. Статус, текст = Self.Тодос [Индекс.ROW ()] # вернуть только текст TODO. Вернуть текст def rowcount (self, index): return len (self.Тодос) 

Чай .Переменная Todos – это наше хранилище данных, а два метода rowCount () и data () – стандартные методы модели, которые мы должны для модели списка. Мы рассмотрим их по очереди ниже.

.Список Тодоса

Хранилище данных для нашей модели .Todos, простой список Python, в котором мы сохраним кортеж значений в формате [(Bool, Str), (Bool, Str), (Bool, Str)], где Bool – это поэтому Состояние данной записи, а STR – текст TODO.

Мы инициализируем себя.Тодо в пустой список в запуске, если список не проходит через аргумент ключевого слова Todos.

себя.todos = todos или [] установит себя.Тодос к предоставленному значению TODOS, если это Трути (я.эн. Что -либо кроме пустого списка, логического ложного или нет значения по умолчанию), в противном случае он будет установлен в пустой список [] .

Чтобы создать Insance этой модели, мы можем просто –

Model = todomodel () # создать пустой список Todo 

Или пройти в существующем списке –

todos = [(false, «элемент»), (false, «другой элемент»)] model = todomodel (todos) 

.RowCount ()

Чай .Метод rowcount () является Callade по просмотру, чтобы получить количество строк в текущих данных. Это требуется для представления, чтобы теперь максимальный индекс, который он может переделать хранилище данных (count-1). Продажа Мы используем список Python в качестве нашего хранилища данных, возвращаемое значение для этого – просто Len () списка.

.Данные ()

Это ядро ​​вашей модели, которая обрабатывает запросы на данные из представления и возвращает соответствующий результат. Он получает два параметра и роли.

Индекс – это положение/координаты данных, которые запрашивает представление, доступно двумя методами .Row () и .Column (), которая дает положение в каждом измерении.

Для нашего QListView столбец всегда 0 и может быть проигнорирован, но вам нужно использовать это для 2D данных в виде электронной таблицы.

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

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

Роль Ценить Описание
QT.Отображать 0 Ключевые данные, которые должны быть развернуты в форме текста. (QString)
QT.Декорация 1 Данные, которые должны быть оформлены как украшение в виде значка. (Qcolor, Qicon или qpixmap)
QT.Редакционная статья 2 Данные в формальном следующем для редактирования в редакторе. (QString)
QT.Tooltiprole 3 Данные, отображаемые в подсказке элемента. (QString)
QT.Statustiprole 4 Данные, отображаемые в строке состояния. (QString)
QT.Что -то это 5 Данные, отображаемые для элемента в «Что это?” мода. (QString)
QT.Sizehintrole 13 Подсказка размера для предмета, который будет предоставлен для просмотров. (QSize)

Для полного списка доступных роли Что вы можете получить, увидеть документацию QT Itmdatarole. Наш список TODO будет использовать только QT.DisplayRole и QT.Декорация .

Основная реализация

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

Sys Sys из Pyqt5 Import qtcore, qtgui, qtwidgets, UIC от pyqt5.Qtcore import qt_creator_file = "mainwindow.UI "UI_MAINWINDOW, QTBASECLASS = UIC.Loaduduype (qt_creator_file) класс todomodel (qtcore.Qabstractlistmodel): def __init __ (self, *args, todos = none, ** kwargs): супер (Todomodel, self).__init __ (*args, ** kwargs).todos = todos или [] def data (self, index, роль): если роль == qt.DisplayRole: Status, Text = Self.Тодос [Индекс.Row ()] возвращается текст def rowcount (self, index): return len (self.TODOS) класс MainWindow (QTWIDGETS.QmainWindow, UI_MainWindow): def __init __ (self): qtwidgets.Qumainwindow.__init __ (self) ui_mainwindow.__init __ (я).Setupui (self) self.Model = todomodel () self.Todview.SetModel (Self.Модель) app = qtwidgets.QAPPLICATION (Sys.Argv) window = mainwindow () окно.Приложение Show ().Exec_ () 

Мы определяем наш домен. В __init__ для MainWindow мы создаем экземпляр нашей модели TODO и устанавливаем эту модель на TODO_VIEW . Сохраните этот файл как todo.Пи и запустить его с –

Python3 Todo.шаг 

Несмотря на то, что еще нечего увидеть, QlistView и наша модель на самом деле работают – если вы добавите некоторые данные по умолчанию, вы увидите, что они появятся в списке.

себя.model = todomodel (todos = [(false, 'my first todo')])))))))))))))))))))))))))))))))))))))) 

QListView с твердым кодированным элементом TODO

QListView с твердым кодированным элементом TODO

Вы можете продолжать добавлять предметы вручную, и они появятся в порядке в QlistView . Далее мы сделаем возможным добавление ITM изнутри приложения.

Сначала создайте новый метод на Mainwindow с именем add . Это наш обратный вызов, который позаботится о добавлении текущего текста с ввода в качестве нового TODO. Подключите этот метод к Addbutton.Нажатый сигнал в конце блока __init__.

Класс Mainwindow (Qtwidgets.QmainWindow, UI_MainWindow): def __init __ (self): qtwidgets.Qumainwindow.__init __ (self) ui_mainwindow.__init __ (я).Setupui (self) self.Model = todomodel () self.Todview.SetModel (Self.Модель) # подключить кнопку. себя.addbutton.нажимать.Соединить (я.Добавить) def Добавить (self): «добавить элемент в наш список Todo, получить текст из QlineEdit .ToDoEdit и там очистить его. "" "Текст = Self.Tododit.Text () Если текст: # Не добавляйте пустые строки. # Доступ к списку через модель. себя.модель.Тодос.Append ((false, Text)) # Trigger Refresh. себя.модель.расположенный.Emit () # опустошить входное я.Tododit.SetText ("") 

В блоке добавить линию самостоятельно.модель.расположенный.Испускают () . Здесь мы излучаем модельный сигнал .расположенный, чтобы дать представлению знать, что форма данные были изменены. Это запускает обновление сущности взгляда. Если вы опустите эту строку, TODO все равно будет добавлен, но QListView не обновит.

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

Подключение других действий

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

 себя.addbutton.нажимать.Соединить (я.Добавить) самостоятельно.Deletebutton.нажимать.Соединить (я.Удалить) Self.Полная пустяка.нажимать.Соединить (я.Полный) 

Затем определите новый метод удаления следующим образом –

 Def delete (self): indexes = self.Todview.SelectedDindexes () Если индексы: # Индексы-это список одного элемента в одном избирате. Index = indexes [0] # Удалить элемент и обновить. Del Self.модель.Тодос [Индекс.Row ()] self.модель.расположенный.Emit () # очистить выбор (так как он недопустим). себя.Todview.Clarseection () 

Мы используем себя.Todview.SelectedDindexes для получения индексов (на самом деле список одного элемента, как мы в режиме с одним выбором), а затем .Row () в качестве индекса в нашем списке Todos на нашей модели. Мы удаляем индексированный элемент, используя оператор DEL Python, а затем запускаем настроенный сигнал, потому что форма данных была изменена.

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

Вы можете попробовать сделать этот умник и выбрать последний элемент в списке вместо этого

Полный метод любит подобное –

 Def offect (self): indexes = self.Todview.SelectredIndexes () Если индексы: index = indexes [0] row = index.Row () статус, текст = self.модель.Todos [row] self.модель.Todos [row] = (true, текст) # .DataChanged берет правое левое и нижнее, которые равны # для одного выбора. себя.модель.DataChanged.Emit (index, index) # очистить выбор (так как он недопустим). себя.Todview.Clarseection () 

Это использует ту же индексацию, что и для удаления, но на этот раз мы берем элемент из модели .Список TODOS, а затем замените статус на True .

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

Ключевое здесь по -разному против. Стандартные виджеты QT заключаются в том, что мы вносим изменения непосредственно на наши данные, и, как только необходимо уведомить QT, что изменяет Haasurd – обновление состояния виджетов обрабатывается автоматически.

Используя Qt.Декорация

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

tick = qtgui.Qimage ('tick.Png ') класс ToDomodel (qtcore.Qabstractlistmodel): def __init __ (self, *args, todos = none, ** kwargs): супер (Todomodel, self).__init __ (*args, ** kwargs).todos = todos или [] def data (self, index, роль): если роль == qt.DisplayRole: _, text = self.Тодос [Индекс.Row ()] возвращает текст, если роль == Qt.Декорация, статус, _ = self.Тодос [Индекс.Row ()] if Status: return tick def rowcount (self, index): return len (self.Тодос) 

Использовали значок клеща.PNG, чтобы указать полные элементы, которые мы загружаем в объект Qimage с именем Tick . В модели мы внедрили обработчик для QT.DecorationRole, которая возвращает значок Tick для строк, статус, статус, правда (для полного).

Значок, который я использую, взята из фьюги, установленной P.Юсукекамиямана

Intostad of Icon I, вы также можете вернуться к цвету, E.г. Qtgui.Qcolor (‘green’), который будет нарисован как твердый квадрат.

Запуск приложения вы теперь сможете пометить элементы как завершенные.

Тодос отмечен завершен

Тодос отмечен завершен

Стойкий хранилище данных

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

Решение состоит в том, чтобы внедрить некоторый выход стойкой хранилища данных. Самый простой подход – это простой хранилище файлов, где мы загружаем элементы из файла JSON или Pickle при запуске и записываем изменение.

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

 Def Load (self): попробуйте: с Open ('data.json ',' r ') как f: self.модель.Todos = json.Загрузить (f) Исключение исключения: Pass Def Save (self): с Open ('data.json ',' w ') как f: data = json.Свалку (я.модель.Todos, f) 

Чтобы сохранить изменения в данных, которые нам нужно добавить .Save () обработчик до конца любого метода, который изменяет данные, и .Загрузка () обработчика в блок __init__ после создания модели.

Окончательный код выглядит так –

Импорт Import Sys JSON из PYQT5 Import QTCORE, QTGUI, QTWIDGETS, UIC от PYQT5.Qtcore import qt_creator_file = "mainwindow.UI "UI_MAINWINDOW, QTBASECLASS = UIC.Loaduduseype (qt_creator_file) tick = qtgui.Qimage ('tick.Png ') класс ToDomodel (qtcore.Qabstractlistmodel): def __init __ (self, *args, todos = none, ** kwargs): супер (Todomodel, self).__init __ (*args, ** kwargs).todos = todos или [] def data (self, index, роль): если роль == qt.DisplayRole: _, text = self.Тодос [Индекс.Row ()] возвращает текст, если роль == Qt.Декорация, статус, _ = self.Тодос [Индекс.Row ()] if Status: return tick def rowcount (self, index): return len (self.TODOS) класс MainWindow (QTWIDGETS.QmainWindow, UI_MainWindow): def __init __ (self): super (mainwindow, self).__В себе.Setupui (self) self.Model = todomodel () self.Load () Self.Todview.SetModel (Self.Модель) я.addbutton.нажимать.Соединить (я.Добавить) самостоятельно.Deletebutton.нажимать.Соединить (я.Удалить) Self.Полная пустяка.нажимать.Соединить (я.Завершите) def Add (self): "" "" Добавить элемент в наш список Todo, получить текст из QlineEdit .ToDoEdit и там очистить его. "" "Текст = Self.Tododit.Text () Если текст: # Не добавляйте пустые строки. # Доступ к списку через модель. себя.модель.Тодос.Append ((false, Text)) # Trigger Refresh. себя.модель.расположенный.Emit () # опустошить входное я.Tododit.SetText ("" ") self.Save () def delete (self): indexes = self.Todview.SelectedDindexes () Если индексы: # Индексы-это список одного элемента в одном избирате. Index = indexes [0] # Удалить элемент и обновить. Del Self.модель.Тодос [Индекс.Row ()] self.модель.расположенный.Emit () # очистить выбор (так как он недопустим). себя.Todview.Clarseection () Self.Save () def heall (self): indexes = self.Todview.SelectredIndexes () Если индексы: index = indexes [0] row = index.Row () статус, текст = self.модель.Todos [row] self.модель.Todos [row] = (true, текст) # .DataChanged берет правое левое и нижнее, которые равны # для одного выбора. себя.модель.DataChanged.Emit (index, index) # очистить выбор (так как он недопустим). себя.Todview.Clarseection () Self.Save () def load (self): попробуйте: с Open ('data.db ',' r ') как f: self.модель.Todos = json.Загрузить (f) Исключение исключения: Pass Def Save (self): с Open ('data.db ',' w ') как f: data = json.Свалку (я.модель.todos, f) app = qtwidgets.QAPPLICATION (Sys.Argv) window = mainwindow () окно.Приложение Show ().Exec_ () 

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

Еще один интересный пример QListView см. В этом примере медиаплеера приложения. Он использует QT Building QMediaPlayList в качестве хранилища данных, с отображением содержимого в QlistView .

О BCR.CX:

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

Описание:

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

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

Создайте следы и настройте свой рабочий процесс

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

Ресурсы:

  1. Создать задачи для каждого билета
  2. Прогресс -бар для выполнения результатов
  3. Создать стандартизированные рабочие процессы для повышения производительности.
  4. Управляйте своим списком задач, чтобы держать его в курсе повседневной жизни.
  1. Стешите как Todo, так и Zendesk Sunshine, чтобы получить полный доступ к функциям приложения
  2. Повышение производительности путем создания или редактирования автоматизаций задач и сохранить персонализированные пресеты.