В данной статье мы создадим десктопное приложение используя PyQt5 и XML API от ЦБ РФ для получения курса валют. Интерфейс нашей программы будет достаточно простой. У нас будут несколько выпадающих списков для выбора дня, месяца и года. После выбора всех настроек, наше приложение сделает запрос к XML API от cbr.ru (Центральный Банк России) для получения курса доллара и евро на указанную дату.
Данный урок нацелен на улучшение знаний полученных после прочтения:
- Парсинг XML с использованием lxml
- Создание, Редактирование и Парсинг XML файла
- Серия уроков по PyQt5
Скачать исходники: скрипт и изображения (33.4 кб)
Каркас приложения
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import (QWidget, QLabel, QApplication, QComboBox, QPushButton) from PyQt5.QtGui import QPixmap, QFont class CBR_API(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setFixedSize(300, 400) self.setWindowTitle('История курса рубля') self.show() if __name__ == '__main__': app = QApplication(sys.argv) money = CBR_API() sys.exit(app.exec_()) |
В данном примере у нас обычный каркас для PyQt5 приложения. Мы указали фиксированный размер для нашего окна. Настроили заголовок приложения.
Лого приложения
Мы быстро создали лого для нашего приложения и загрузили её в PyQt5 с помощью QPixmap. Вот так выглядит метод initUI после добавления лого.
1 2 3 4 5 6 7 8 9 |
def initUI(self): # Загружаем лого нашей программы. logo_label = QLabel(self) logo_label.setPixmap(QPixmap("img/logo.png")) logo_label.move(0, 0) self.setFixedSize(300, 400) self.setWindowTitle('История курса рубля') self.show() |
Запускаем наш скрипт.
Выпадающие списки в PyQt5
После загрузки лого, нам понадобятся небольшие настройки для выбора дня, месяца и года. Тем самым, мы сформируем правильный запрос к API cbr.ru. Для создания выпадающих списков вы воспользуемся классом QComboBox из под-модуля PyQt5.QtWidgets.
Мы создадим 3 новых метода метода:
- self.days() — создаем выпадающий список дней (1-30).
- self.month() — создает выпадающий список номеров месяцев (1-12).
- self.year() — создаем выпадающий список годов начиная с 2005.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
def days(self): """ Выпадающий список дней. """ # Создаем выпадающий список. self.days_combo = QComboBox(self) # Заголовок списка. day_label = QLabel("День", self) day_label.move(20, 170) for day in range(1, 31): # Наполняем список. self.days_combo.addItem('%d' % day) # Фиксируем список. self.days_combo.move(20, 200) def month(self): """ Выпадающий список месяцев. """ # Создаем выпадающий список. self.month_combo = QComboBox(self) # Заголовок списка. month_label = QLabel("Месяц", self) month_label.move(80, 170) for month_num in range(1, 13): # Наполняем список. self.month_combo.addItem('%d' % month_num) # Фиксируем список. self.month_combo.move(80, 200) def year(self): """ Выпадающий список годов. """ # Создаем выпадающий список. self.year_combo = QComboBox(self) # Заголовок списка. month_label = QLabel("Год", self) month_label.move(140, 170) for year_num in range(2005, 2018): # Наполняем список. self.year_combo.addItem('%d' % year_num) # Фиксируем список. self.year_combo.move(140, 200) |
После создания методов внутри класса CBR_API мы должны вызвать методы в initUI. Мы создадим и кнопку «OK» после нажатия на которую выполнится запрос к API банка.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
def initUI(self): # Загружаем лого нашей программы. logo_label = QLabel(self) logo_label.setPixmap(QPixmap("img/logo.png")) logo_label.move(0, 0) # Загружаем выпадающие списки для дней, месяцев и годов. self.days() self.month() self.year() # Создаем кнопку "OK". ok_button = QPushButton('ОК', self) ok_button.resize(50, 25) ok_button.move(220, 200) # Каждый клик кнопки вызывает метод "makeRequest" ok_button.clicked.connect(self.makeRequest) self.setFixedSize(300, 400) self.setWindowTitle('История курса рубля') self.show() def makeRequest(self): return True |
Запускаем код и у нас должен появится вот такой результат.
Вывод курса валют
У нас слишком много места под выпадающими списками, нужно их заполнить иконками доллара и евро. Так-же, мы изменим шрифт текста для курса валют. Текст с курсом валют будет обновляться каждый раз как мы нажмем на кнопку «OK». Создаем метод load_result_image который загрузит иконки и выполнит позиционирование текста для каждой валюты.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
def load_result_image(self): # Настройки шрифта текста. font = QFont() font.setFamily("Arial") font.setPointSize(18) # Загружаем иконку доллара. dollar_label = QLabel(self) dollar_label.setPixmap(QPixmap("img/dollar.png")) dollar_label.move(60, 260) # Текст с выводом курса доллара. self.dollar_value = QLabel("0 руб.", self) self.dollar_value.setFont(font) self.dollar_value.move(130, 263) # Загружаем иконку евро. euro_label = QLabel(self) euro_label.setPixmap(QPixmap("img/euro.png")) euro_label.move(50, 320) # Текст с выводом курса евро. self.euro_value = QLabel("0 руб.", self) self.euro_value.setFont(font) self.euro_value.move(130, 320) |
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Не забываем добавить вызов данного метода в initUI. После чего запускаем наш код.
Backend приложения
Интерфейс нашего приложения готов. Теперь, нужно доделать и backend часть, для этого придерживаемся данного сценария:
- Пользователь выбирает нужный день, месяц, год
- Нажимает на «ОК«
- Выполняется makeRequest который использует библиотеку Requests на примерах.
- Собираем данные из выпадающих списках
- Формируем запрос к XML API от cbr.ru
- Выполняем парсинг полученных XML данных
- Обновляем текст с курсом валют
- ???
- Profit!
Для начала создадим недостающие методы:
- makeRequest — получает данные из выпадающих списков и отправляет их в getResult.
- getResult — выполняет запрос к XML cbr.ru и обрабатывает полученные данные.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
def getResult(self, day, month, year): """ Выполняет запрос к API Банка России. :param day: Выбранный день. :param month: Выбранный номер месяца. :param year: Выбранный код :return: dict """ result = { 'usd': 0, 'eur': 0, } if int(day) < 10: day = '0%s' % day if int(month) < 10: month = '0%s' % month try: # Выполняем запрос к API. get_xml = requests.get( 'http://www.cbr.ru/scripts/XML_daily.asp?date_req=%s/%s/%s' % (day, month, year) ) # Парсинг XML используя ElementTree structure = ET.fromstring(get_xml.content) except: return result try: # Поиск курса доллара (USD ID: R01235) dollar = structure.find("./*[@ID='R01235']/Value") result['dollar'] = dollar.text.replace(',', '.') except: result['dollar'] = 'x' try: # Поиск курса евро (EUR ID: R01239) euro = structure.find("./*[@ID='R01239']/Value") result['euro'] = euro.text.replace(',', '.') except: result['euro'] = 'x' return result def makeRequest(self): """ После нажатия на "ОК" выполняется запрос к API с выбранными данными. """ # Получаем текущие значения из выпадающих списках. day_value = self.days_combo.currentText() month_value = self.month_combo.currentText() year_value = self.year_combo.currentText() # Выполняем запрос к API с выбранными данными. result = self.getResult(day_value, month_value, year_value) # Заменяем текст для доллара. self.dollar_value.setText('%s руб.' % result['dollar']) self.dollar_value.adjustSize() # Заменяем текст для евро. self.euro_value.setText('%s руб.' % result['euro']) self.euro_value.adjustSize() |
Структура XML ответа от cbr.ru
Скрипт выполняет запрос к URL:
1 |
http://www.cbr.ru/scripts/XML_daily.asp?date_req=ДД/ММ/ГГГ |
Получаем такой ответ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<ValCurs Date="22.04.2017" name="Foreign Currency Market"> <Valute ID="R01235"> <NumCode>840</NumCode> <CharCode>USD</CharCode> <Nominal>1</Nominal> <Name>Доллар США</Name> <Value>56,2307</Value> </Valute> <Valute ID="R01239"> <NumCode>978</NumCode> <CharCode>EUR</CharCode> <Nominal>1</Nominal> <Name>Евро</Name> <Value>60,3187</Value> </Valute> </ValCurs> |
Мы получим более длинный список, но для экономии места показываем только для евро и доллара. Можете заметить, что каждая валюта имеет свой уникальный идентификационный номер.
- ID Доллара: R01235
- ID Евро: R01239
По данному идентификационному номеру мы и выполнили поиск нужной нам валюты используя библиотеку ElementTree и метод find.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Выполняем запрос к API. get_xml = requests.get( 'http://www.cbr.ru/scripts/XML_daily.asp?date_req=%s/%s/%s' % (day, month, year) ) # Парсинг XML используя ElementTree structure = ET.fromstring(get_xml.content) # Поиск курса доллара (USD ID: R01235) dollar = structure.find("./*[@ID='R01235']/Value") result['dollar'] = dollar.text.replace(',', '.') # Поиск курса евро (EUR ID: R01239) euro = structure.find("./*[@ID='R01239']/Value") result['euro'] = euro.text.replace(',', '.') |
После получения курса мы заменяем текст для каждой валюты.
1 2 3 4 5 6 7 |
# Заменяем текст для доллара. self.dollar_value.setText('%s руб.' % result['dollar']) self.dollar_value.adjustSize() # Заменяем текст для евро. self.euro_value.setText('%s руб.' % result['euro']) self.euro_value.adjustSize() |
Давайте узнаем какой сейчас курс рубля!
Благодарю за внимание!
Если есть вопросы — прошу в комментариях.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»