Меню
Видеоучебник
Видеоучебник  /  Информатика  /  Основы алгоритмизации и программирования на языке Python  /  Представление в приложении с графическим интерфейсом

Представление в приложении с графическим интерфейсом

Урок 34. Основы алгоритмизации и программирования на языке Python

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

Конспект урока "Представление в приложении с графическим интерфейсом"

Вопросы:

·     Представление приложения «Калькулятор».

·     Контроллер.

Ранее мы начали решать задачу. Вспомним её условие. Нужно создать приложение «Калькулятор» для выполнения основных арифметических операций над целыми числами.

Мы уже создали модель обработки данных для нашего калькулятора в модуле calcModel. Вся модель описана в классе calc. По сути это и есть калькулятор, правда пока он управляется только через текстовый интерфейс среды разработки языка Python. На этом уроке не имеет значения каким образом мы реализовали модель. Нам лишь важно знать, что она описана в классе calc. У этого класса есть доступное для чтения поле display, которое содержит данные, отображаемые на дисплее калькулятора. А также есть общедоступный метод pressButton, который имитирует нажатие на кнопку калькулятора. Этот метод принимает на вход текст кнопки.

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

Начнём описание интерфейса калькулятора. В одной папке с моделью калькулятора создадим модуль с именем calcGui. В нём мы опишем графический интерфейс калькулятора и его работу. Сначала импортируем в модуль все необходимые классы. Подключим модуль sys. Затем из библиотеки PyQt5: из модуля QtWidgets загрузим все классы, из модуля QtCore загрузим класс Qt, для управления выравниванием текста на дисплее, а также из модуля QtGui загрузим класс QFont для работы со шрифтами и класс QIcon для того, чтобы установить значок приложения.

import sys

from PyQt5.QtWidgets import *

from PyQt5.QtCore import Qt

from PyQt5.QtGui import QFont, QIcon

Теперь создадим класс calcWindow, который будет наследником класса QWidget. Опишем его содержимое позже. Запишем команды для запуска приложения. В основной программе в переменной a создадим объект класса QApplication. При вызове конструктора класса в качестве аргумента укажем значение argv из модуля sys, то есть параметры командной строки. Далее в переменной window создадим объект класса calcWindow, то есть окно калькулятора. После чего выведем созданное окно на экран, вызвав у него метод show без параметров. И наконец опишем завершение работы приложения. Для этого из модуля sys вызовем функцию exit, в которой в качестве параметра укажем метод exec, вызванный у объекта a.

class calcWindow (QWidget):

    pass   

 

a = QApplication (sys.argv)

window = calcWindow ()

window.show ()

sys.exit (a.exec ())

Теперь перейдём к описанию класса calcWindow. Опишем его конструктор. В нём сперва вызовем метод-конструктор класса-предка – QWidget. И вызовем метод setupUI, который опишем далее.

def __init__ (self):

    super ().__init__ ()

    self.clc = calc ()

    self.setupUI ()

В методе setupUI изменим заголовок окна на слово «Калькулятор», вызвав у него метод setWindowTitle. Теперь установим значок окна. Для этого вызовем у него метод setWindowIcon. В этом методе вызовем конструктор класса QIcon. В качестве значка выберем файл calcIcon.png, который находится в одном файле с приложением.

def setupUI (self):

    self.setWindowTitle ('Калькулятор')

    self.setWindowIcon (QIcon ('calcIcon.png'))

Далее создадим в поле lblDisplay дисплей калькулятора и настроим его. Дисплей калькулятора будет меткой, то есть объектом класса QLabel. При создании укажем текст дисплея, в начальный момент времени это будет ноль. Установим белый цвет дисплея. Для этого у поля lblDisplay, вызовем метод setStyleSheet. В этом методе опишем в символьной строке стиль дисплея. Он будет состоять из слова background-color, двоеточия и вызова функции rgb, в котором укажем белый цвет, то есть 3 числа 255. Далее установим выравнивание текста на дисплее, вызвав у него метод setAlignment. Выравнивание текста описано в модуле Qt. В методе укажем горизонтальное выравнивание текста на дисплее по правому краю, а вертикальное – по центру. Установим шрифт дисплея, вызвав у него метод setFont. В этом методе вызовем конструктор класса QFont, в котором укажем шрифт «Calibri», размером 24 пункта.

self.lblDisplay = QLabel (self.clc.display, self)

self.lblDisplay.setStyleSheet ('background-color: rgb(255, 255, 255)')

self.lblDisplay.setAlignment (Qt.AlignRight | Qt.AlignCenter)

self.lblDisplay.setFont (QFont ('Calibri', 24))

Чтобы расположение виджетов и их размер автоматически изменялись при изменении размеров окна, в переменной grid, создадим объект класса QGridLayout, то есть сеточный макет графического интерфейса окна.  Добавим в нулевую строку этого макета созданный дисплей, начиная с ячейки ноль, ноль. Дисплей будет занимать одну ячейку макета по вертикали и четыре ячейки – по горизонтали.

grid = QGridLayout ()

grid.addWidget (self.lblDisplay, 0, 0, 1, 4)

Теперь перейдём к созданию кнопок. Их у нашего калькулятора шестнадцать и если мы будем создавать и настраивать каждую кнопку по отдельности – это займёт много времени и код получится очень громоздким. Поэтому мы поступим хитрее и опишем создание и настройку кнопок в цикле. Но сперва нужно где-то сохранить текст кнопок. Для этого создадим в переменной btntxt двумерный массив из четырёх строк и четырёх столбцов, по количеству кнопок калькулятора в котором будет хранится текст, отображаемый на кнопках. В нулевой строке массива будут цифры «7», «8», «9» и буква «C» для кнопки сброса, то есть текст первой строки кнопок. Точно также заполним текстом и остальные три строки матрицы.

btntxt = [['7', '8', '9', 'C'],

          ['4', '5', '6', '*'],

          ['1', '2', '3', '/'],

          ['0', '+', '-', '=']]

Далее будем создавать и настраивать сами кнопки для этого опишем цикл с параметром i, в котором будем перебирать индексы строк, созданного нами массива. Этот параметр будет изменяться от 0 до 3. В этом цикле будет следовать ещё один цикл с параметром j, в котором будем перебирать индексы столбцов массива.  Он также будет изменяться от 0 до 0. В этом цикле в переменной button будем создавать объект класса QPushButton, то есть кнопку с текстом, который будем брать из созданного ранее массива из ячейки с индексами i и j. С помощью метода setFont установим шрифт кнопок calibri размером 20 пунктов. После этого будем настраивать политику размеров кнопок, ведь по умолчанию вертикальный размер кнопки зафиксирован, а это нам не подходит. Вызовем у кнопки метод setSizePolicy. Политика размеров описана в классе QSizePolicy. Установим предпочтительные горизонтальный и вертикальный размеры кнопки, то есть Preferred, чтобы горизонтальный и вертикальный размеры кнопки автоматически изменялись вместе с размерами окна. Свяжем у кнопок событие clicked с методом calculate, который опишем позже. Теперь нужно добавить созданную кнопку на макет окна. Будем добавлять кнопки на позицию с индексами i + 1 и j, так как нулевая строка ячеек макета занята дисплеем калькулятора.  Таким образом мы разместили на форме все кнопки и настроили их.

for i in range (4):

    for j in range (4):

        button = QPushButton (btntxt[i][j], self)

        button.setFont (QFont ('Calibri', 20))

        button.setSizePolicy (QSizePolicy.Preferred, QSizePolicy.Preferred)

        button.clicked.connect (self.calculate)

        grid.addWidget (button, i + 1, j)

После циклов добавим между ячейками макета свободное пространство. Для этого вызовем у макета метод setSpacing. Установим расстояние между ячейками в 10 пикселей. Наконец отобразим макет на форме. Для этого вызовем у формы метод setLayout, в котором в качестве параметра укажем созданный макет – grid.

grid.setSpacing (10)

self.setLayout (grid)

Создадим в классе метод calculate, но пока в нём ничего делать не будем.

def calculate (self):

    pass

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

Чтобы это изменить нужно подключить модель, которую мы описали ранее. Для этого в начале модуля, при загрузке и подключении классов загрузим ещё из модуля calcModel класс calc. Дальше в конструкторе класса calcWindow, создадим ещё одно поле, которое назовём clc. В этом поле создадим объект класса calc. Теперь перейдём к строке, в которой мы создали дисплей калькулятора. В начальный момент времени на дисплее действительно будет цифра ноль, но теперь мы возьмём эту цифру у объекта clc из поля display, то есть из модели калькулятора.

Остаётся лишь описать метод calculate, который будет обработчиком нажатий на кнопки калькулятора. Сначала вызовем у поля clc метод pressButton, в котором в качестве параметра укажем текст нажатой кнопки. То есть мы задаём в модель нажатие на кнопку. После того, как модель обработала это нажатие, у поля lblDisplay нужно установить текст и с дисплея модели, то есть из свойства display поля clc. Описание модуля завершено.

import sys

from PyQt5.QtWidgets import *

from PyQt5.QtCore import Qt

from PyQt5.QtGui import QFont, QIcon

from calcModel import calc

 

class CalcWindow (QWidget):

    def __init__ (self):

        super ().__init__ ()

        self.clc = calc ()

        self.setupUI ()

 

    def setupUI (self):

        self.setWindowTitle ('Калькулятор')

        self.setWindowIcon (QIcon ('calcIcon.png'))

        self.lblDisplay = QLabel (self.clc.display, self)

        self.lblDisplay.setStyleSheet ('background-color: rgb(255, 255, 255)')

        self.lblDisplay.setAlignment (Qt.AlignRight | Qt.AlignCenter)

        self.lblDisplay.setFont (QFont ('Calibri', 24))

        grid = QGridLayout ()

        grid.addWidget (self.lblDisplay, 0, 0, 1, 4)

        btntxt = [['7', '8', '9', 'C'],

                  ['4', '5', '6', '*'],

                  ['1', '2', '3', '/'],

                  ['0', '+', '-', '=']]

        for i in range (4):

            for j in range (4):

                button = QPushButton (btntxt[i][j], self)

                button.setFont (QFont ('Calibri', 20))

                button.setSizePolicy (QSizePolicy.Preferred, QSizePolicy.Preferred)

                button.clicked.connect (self.calculate)

                grid.addWidget (button, i + 1, j)

        grid.setSpacing (10)

        self.setLayout (grid)

 

    def calculate (self):

        self.clc.pressButton (self.sender ().text ())

        self.lblDisplay.setText (self.clc.display)

       

 

a = QApplication (sys.argv)

window = CalcWindow ()

window.show ()

sys.exit (a.exec ())

Запустим программу на выполнение. На экране появился интерфейс калькулятора. Его элементы управления подстраиваются под изменяющиеся размеры окна. Проверим работу калькулятора. Вычислим сумму чисел 20 и 30. Она действительно равна 50. Умножим полученный результат на 2 и получим 100. Теперь нажмём «сброс». На дисплее калькулятора, как в начале работы появилось число ноль. Вычислим разность 5 и 7. Она действительно равна -2. Попробуем разделить полученный результат на 0. На дисплей было выведено сообщение о том, что на ноль делить нельзя. При нажатии на кнопку с цифрой, это сообщение исчезает и мы можем продолжить работу с приложением. Программа работает правильно. Задача решена.

Таким образом вся логика работы калькулятора у нас описана в модуле calcModel, а его интерфейс в модуле calcGui. При этом модуль calcModel не зависит от графического интерфейса калькулятора.

Иногда помимо модели обработки данных и представления в приложении выделяют ещё и третий функциональный блок –контроллер. Обычно он интерпретирует действия пользователя и вызывает соответствующие части модели, а также осуществляет начальную обработку входных данных перед их передачей в модель. Однако контроллер обычно выделяется лишь в сложных приложениях. Чаще всего управление моделью описывается в блоке представления, в обработчиках событий. В нашем приложении роль контроллера выполняет единственная инструкция в методе cal, вызывающая у модели калькулятора метод pressButton.

Мы узнали:

·     Представление описывает графический интерфейс приложения и его работу.

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

0
1191

Комментарии 0

Чтобы добавить комментарий зарегистрируйтесь или на сайт