Пользовательский интерфейс

Для построения пользовательского интерфейса библиотека Qt, поставляемая в рамках ГИС Аксиома, предоставляет большой набор инструментария.

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

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

from axipy import *
from PySide2.QtWidgets import QDialog

class MyDialog(QDialog):
    pass

dialog = MyDialog(view_manager.global_parent)
dialog.resize(500,300)
dialog.exec()

Здесь мы создали пользовательский класс диалога и активировали его. Отметим, что свойство view_manager.global_parent, переданное в конструктор рассматривается как объект-владелец данного диалога. Если данное свойство не проставить, то диалог в панели задач будет показан как отдельное приложение.

Тестирование можно производить в рамках самой ГИС Аксиомы. Для этого необходимо открыть редактор. Кнопка запуска находится на панели «Консоль Python». Если она отсутствует в интерфейсе, то для включения необходимо в выпадающем меню кнопки «Панели» включить пункт «Консоль Python». Сохраним данный файл в файловой системе под именем mydialog.py.

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

  • Создать эти элементы программно

  • Использовать для этого файл ресурсов *.ui

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

Программное наполнение диалога

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

from axipy import *
from PySide2.QtWidgets import QDialog, QPushButton, QLineEdit

class MyDialog(QDialog):
    def __init__(self, parent):
        super().__init__(parent)
        self.button = QPushButton(self)
        self.button.setText("Кнопка")
        self.button.move(150,50)
        # Реакция на нажатие кнопки
        self.button.clicked.connect(self.button_clicked)
        self.edit = QLineEdit(self)
        self.edit.setText("Текст")
        self.edit.move(10,50)

    def button_clicked(self) :
        print(self.edit.text())

dialog = MyDialog(view_manager.global_parent)
dialog.resize(500,300)

dialog.exec()

В данном примере элементы располагаются с явным указанием их места. Это не совсем удобно и в зависимости от размеров элементов управления на конкретной системе могут накладываться друг на друга. Или при изменении размеров самой формы могут вообще исчезать. Для решения этой проблемы обычно используются классы динамического размещения элементов на форме. В нашем случае это наследники класса PySide2.QtWidgets.QLayout. Модифицируем пример с использованием класса PySide2.QtWidgets.QGridLayout.

from axipy import *
from PySide2.QtWidgets import QDialog, QPushButton, QLineEdit, QGridLayout

class MyDialog(QDialog):
    def __init__(self, parent):
        super().__init__(parent)
        # создаем layout
        layout = QGridLayout()
        self.button = QPushButton()
        self.button.setText("Кнопка")
        self.button.clicked.connect(self.button_clicked)
        # Добавляем кнопку
        layout.addWidget(self.button, 0, 0, Qt.AlignTop)
        self.edit = QLineEdit()
        self.edit.setText("Текст")
        # Добавляем элемент ввода
        layout.addWidget(self.edit, 0, 1, Qt.AlignTop)
        # Назначаем layout для диалога
        self.setLayout(layout)

    def button_clicked(self) :
        print(self.edit.text())

dialog = MyDialog(view_manager.global_parent)
dialog.resize(500,300)

dialog.exec()

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

Если элементов на форме много, то такой способ размещения элементов достаточно трудоемок. Рассмотрим альтернативный способ размещения с использованием дизайнера Qt Designer.

Использование файла ресурсов *.ui

Для формирования UI представления формы нам понадобится инструмент Qt Designer. Он поставляется совместно со средствами разработки Qt, но его также можно установить отдельно как пакет python qt5_applications. Подробнее о инсталляции пакетов см в разделе Без интернета. Ручная установка пакетов.. Т.е. в конечном итоге в зависимости от окружения нам нужно выполнить команду:

python3 -m pip install --user qt5_applications

После успешной инсталляции в каталог site-packages из файлового менеджера переходим в каталог site-packages/qt5_applications/Qt/bin и запускаем на выполнение файл designer.

Выбираем тип диалога или формы. В нашем случае это Dialog without buttons. помещаем на него посредством перетаскивания кнопку PySide2.QtWidgets.QPushButton и элемент ввода PySide2.QtWidgets.QLineEdit. Меняем у кнопки ее текст на «Кнопка» (двойным щелчком на элементах) и для поля ввода заносим текст «Текст». По правой кнопке мыши можно поменять названия элементов на button и edit (чтобы они совпадали с предыдущими примерами). Далее, можно применить динамическое размещение элементов, выбрав на панели его тип. Для этого выберем мышью фарсу и нажмем кнопку на панели Lay Out in a Grid. Чтобы элементы прижать кверху, перетянем по аналогии с кнопкой и полем ввода элемент Vertical Spacer и отпустим его над нижней частью окна. После этого наши элементы должны с середины переместиться наверх. Сохраняем файл под именем mydialog.ui и кладем его рядом с файлом mydialog.py.

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

from axipy import *
from PySide2.QtWidgets import QDialog, QPushButton, QLineEdit, QVBoxLayout
from PySide2.QtUiTools import QUiLoader
import os

class MyDialog(QDialog):
    def __init__(self , parent):
        super().__init__(parent)
        # Путь файла в файловой системе
        uiFile = os.path.join(os.path.dirname(__file__), "mydialog.ui")
        # Загружаем файл ui
        self.ui = QUiLoader().load(uiFile, parent)
        self.ui.button.clicked.connect(self.button_clicked)

    def button_clicked(self) :
        print(self.ui.edit.text())

    # Перенаправим метод show на self.ui
    def show(self):
        return self.ui.show()

# Показ диалога как немодальное окно. Для модального используем метод exec
dialog = MyDialog(view_manager.global_parent)
dialog.resize(500,300)
dialog.show()

Стоит заметить, что к элементам теперь необходимо обращаться не через self, а через self.ui. В примерах поставки ГИС аксиомы есть подобное решение с реализацией в виде модуля ru_axioma_gis_axipy_example_dialog.