Многие используют в своей работе Jupyter Notebooks. Но с ростом сложности проекта появляются проблемы. В блокноте появляются ячейки с красными пометками для самого себя «перед запуском укажи число...» или «задай количество итераций исходя из...». Какой-то откат к командной строке получается.
Да и вывод данных на экран не всегда воспринимается без пояснений сторонним человеком, который привык к красивым таблицам, картинкам и прочим современным элементам интерфейса.
Например, у нас есть данные по площадям городов и численности населения. Выведем их на экран в «традиционном виде»:
tabledata = [["Москва", 2561, 12615882],
["Санкт-Петербург", 1439, 5383890],
["Ярославль", 205, 609828],
["Хабаровск", 383, 1321473]]
tabledata
Видим в блокноте:
[['Москва', 2561, 12615882],
['Санкт-Петербург', 1439, 5383890],
['Ярославль', 205, 609828],
['Хабаровск', 383, 1321473]]
Современному, избалованному человеку такой формат отображения не всегда нравится. Нужно привести данные к более привычному табличному виду.
Можно использовать широко распространенную библиотеку pandas
import pandas as pd
pd.DataFrame(tabledata, columns=["Город","Площадь (кв. км)", "Население (человек)"])
Если по каким-то причинам использование pandas не устраивает, можно воспользоваться другой библиотекой или написать свою функцию.
Рассмотрим одну из таких библиотек — tabulate (https://pypi.org/project/tabulate/)
Для установки запустите из командной строки pip install tabulate
from IPython.display import HTML, display
from tabulate import tabulate
display(HTML(tabulate(tabledata, tablefmt='html')))
Можно вывести данные в «псевдографическом» виде.
print(tabulate(tabledata))
Можно добавить заголовки
print(tabulate(tabledata, headers=["Город","Площадь (кв. км)", "Население (человек)"]))
И индексы
display(HTML(tabulate(tabledata, headers=["Город","Площадь (кв. км)", "Население (человек)"], tablefmt='html', showindex="always")))
tabulate позволяет получить визуально такой же результат как и pandas.
Можно написать свою функцию, которая потом обрастёт дополнительными возможностями.
from IPython.display import HTML, display
def dataToTable(data, columns = None):
if len(data) == 0 :
display(HTML('<b>Нет данных</b>'))
return
hdr = ''
if columns != None:
for col in columns: # Формируем заголовок таблицы
hdr = hdr + '<th>' + col + '</th>'
hdr = '<tr>' + hdr + '</tr>'
dt = ''
for row in data: # Проходим циклом по всем строкам
dt = dt + '<tr>'
for cell in row: # И формируем тело таблицы
dt = dt + '<td>' + str(cell) + '</td>'
dt = dt + '</tr>'
display(HTML('<table>' + hdr + dt + '</table>')) # Выводим таблицу на экран
dataToTable(tabledata, columns=["Город","Площадь (кв. км)", "Население (человек)"])
Вывод изображений
Мы привыкли к пиктограммам и иконкам. Даже в прогнозе погоды мы видим картинки с солнышками и тучками. Чтобы добавить изображения в наши программы можно использовать библиотеку IPython. Ее функция Image позволяет работать с изображениями (PNG/JPEG/GIF), размещенными как локально, так и на интернет-ресурсах. Задавать их размеры.
Описание библиотеки здесь ipython.readthedocs.io/en/stable/api/generated/IPython.display.html?highlight=display#IPython.display.Image
from IPython.display import Image # Библиотека для отображения картинок
display(Image(url='https://habrastorage.org/webt/9m/2c/zd/9m2czdt-uv7oe6v-nws3frtw7-a.jpeg',
width = 200) # Задаем ширину картинки
)
# display(Image(filename='Python_royal_35.JPG', width = 200)) # Локальный файл
Любуемся питоном:
Украшаем текст
Конечно, можно генерировать HTML напрямую, используя все его возможности:
from IPython.core.display import display, HTML
display(HTML("<font color='green'>Мой зеленый текст</font>"))
А можно воспользоваться библиотекой termcolor. Она позмоляет не углубляясь в HTML задавать цвет текста и фона, задавать атрибуты шрифта. Описание библиотеки тут — pypi.org/project/termcolor
from termcolor import colored # Для установки запустите из командной строки pip install termcolor
print(colored("Красный текст (без атрибутов)", "red"))
print(colored("Красный текст (подчеркивание)", "red", attrs=["underline"]))
print(colored("Зеленый текст на красном фоне (без атрибутов)", "green", "on_red" ))
print(colored("Зеленый текст на красном фоне (bold)", "green", "on_red", attrs=["bold"]))
Отображаем прогресс выполнения задачи
Никому не нравится следить за ходом выполнения длительной задачи не понимая какая часть работы уже выполнена.
Видеть сколько осталось — гораздо приятнее (да-да, знаю, что скорость движения «червяка» может меняться).
Описание библиотеки тут — ipywidgets.readthedocs.io
Для установки используйте команды
pip install ipywidgets
jupyter nbextension enable --py widgetsnbextension
from ipywidgets import IntProgress
from IPython.display import display
import time
prgBar = IntProgress(min = 0, max = 100) # Создаем прогрессбар
display(prgBar) # Выводим прогрессбар на экран
while prgBar.value < prgBar.max: # Пока положение не дошло до максимума - продолжаем цикл
prgBar.value = prgBar.value + 1 # Двигаем "полоску"
time.sleep(0.1)
print('Процесс завершен')
Интерактивное взаимодействие с пользователем
Та же библиотека ipywidgets позволяет не только отображать, но и вносить информацию.
Самый, наверное, простой пример взаимодействия с пользователем — это реакция на нажатие кнопки. Библиотека ipywidgets позволяет создать кнопку с заданными параметрами (текстом, стилем и размерами) и назначить функцию-обработчик ее нажатия.
from IPython.display import display
from ipywidgets import Button
# Создаем кнопку с нужными параметрами
button1 = Button(description="Нажми меня!",
button_style='success' # 'success', 'info', 'warning', 'danger', ''
)
def on_button_clicked(b): # Описываем обработчик события
print("Клик")
button1.on_click(on_button_clicked) # Назначаем этот обработчик на событие "on_click"
display(button1) # Отображаем кнопку
Размер кнопки задается при помощи свойства layout
from IPython.display import display
from ipywidgets import Button, Layout
button2 = Button(description='Кнопка с заданными размерами', button_style='success',
layout=Layout(width='50%', height='80px'))
display(button2)
Для удобного ввода пользователем чисел и дат есть компоненты FloatSlider и DatePicker.
Чтобы получить введенное значение — используется свойство <компонент>.value
Чтобы отловить момент изменения значений, нужно использовать событие observe
from IPython.display import display
from ipywidgets import FloatSlider
fSlider = FloatSlider(
value=7.5, # Первоначальное значение
min=0, # Минимум
max=10.0, # Максимум
step=0.1, # Шаг изменения
description='Параметр:',
continuous_update=False, # True - событие observe возникает для каждого шага при изменении значения
orientation='horizontal' # Горизонтальное или вертикальное расположение
)
def on_value_change(b):
print(b['old'], '->', b['new'])
fSlider.observe(on_value_change, names='value')
display(fSlider)
Проверим доступ к текущему значению:
fSlider.value
Интерактивный календарь:
from IPython.display import display
from ipywidgets import DatePicker
dPicker = DatePicker(
description='Дата:'
)
def on_button_clicked(b):
print(b['old'], '->', b['new'])
dPicker.observe(on_button_clicked, names='value')
display(dPicker)
Для выбора одного значения из нескольких вариантов есть список RadioButtons, выпадающий список Dropdown и группа кнопок ToggleButtons. value и observe для этих компонентов используются точно так же.
Значения можно задавать как в виде перечня строчных величин, так и в виде списка кортежей.
Попробуем самый простой вариант, со значениями в виде списка строк.
from IPython.display import display
from ipywidgets import RadioButtons
rButtons1 = RadioButtons(
options=['Красный', 'Желтый', 'Зеленый'],
value='Желтый', # Выбор по умолчанию
description='Цвет:'
)
def on_button_clicked(b):
print(b['old'], '->', b['new'])
rButtons1.observe(on_button_clicked, names='value')
display(rButtons1)
Выведем на экран значение:
rButtons1.value
В этом режиме значением rButtons1.value является строка.
Пробуем второй вариант задания списка значений:
from IPython.display import display
from ipywidgets import RadioButtons
rButtons2 = RadioButtons(
options=[('Красный', 1), ('Желтый', 2), ('Зеленый', 3)],
value=2, # Выбор по умолчанию
description='Цвет:'
)
def on_button_clicked(b):
print(b['old'], '->', b['new'])
rButtons2.observe(on_button_clicked, names='value')
display(rButtons2)
В этом режиме значением rButtons2.value является число, соответствующее выбранному значению.
Аналогично работает выпадающий список (Dropdown)
from IPython.display import display
from ipywidgets import Dropdown
dropdown1 = Dropdown(
options=[('Красный', 1), ('Желтый', 2), ('Зеленый', 3)],
value=2, # Выбор по умолчанию
description='Цвет:'
)
def on_button_clicked(b):
print(b['old'], '->', b['new'])
dropdown1.observe(on_button_clicked, names='value')
display(dropdown1)
Для ввода булевых значений можно использовать Checkbox и ToggleButton. У них есть уже знакомые нам value и observe.
from IPython.display import display
from ipywidgets import Checkbox
cb1 = Checkbox(
value=False,
description='Согласен'
)
def on_button_clicked(b):
print(cb1.value)
cb1.observe(on_button_clicked, names='value')
display(cb1)
from IPython.display import display
from ipywidgets import ToggleButton
tb1 = ToggleButton(
value=False,
description='Не нажата',
disabled=False,
button_style='success', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Принять условия',
icon='check'
)
tb2 = ToggleButton(
value=True,
description='А эта нажата',
disabled=False,
button_style='success', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Принять условия',
icon='check'
)
display(tb1, tb2)
Для ввода многострочного текста служет компонент Textarea
from IPython.display import display
from ipywidgets import Textarea, Layout
Textarea1 = Textarea(
value='Привет, Habr!',
placeholder='Введите текст',
description='Текст:',
layout=Layout(width='600px', height='100px')
)
display(Textarea1)
ИИ (интерфейсные изыски)
Когда элементов интерфейса становится слишком много, хочется пойти по пути десктопных приложений и объединить отдельные элементы в обособленные группы.
Для этого нам пригодятся Accordion и Tab.
from IPython.display import display
from ipywidgets import Accordion, IntSlider, Text
accordion = Accordion(children=[IntSlider(value=42), Text(value='Сорок два')])
accordion.set_title(0, 'Раздел 1')
accordion.set_title(1, 'Раздел 2')
display(accordion)
from IPython.display import display
from ipywidgets import Tab, IntSlider, Text
tab = Tab()
tab.children = [IntSlider(value=42), Text(value='Сорок два')]
tab.set_title(0, 'Раздел 1')
tab.set_title(1, 'Раздел 2')
display(tab)
Библиотека ipywidgets не ограничивается элементами, которые я перечислил. В ней еще масса полезных вещей, которые могут сделать программы в Jupyter Notebooks более привлекательными.
Автор: Кулюкин Олег