Доброго времени суток!
Сегодня я вас научу делать каталог, на примере одной категории.
Каталог будет уметь отправлять все фото из категории с описанием и ценой, либо отправлять парочку блюд с возможностью пролистывания, насколько это возможно в боте.
Также покажу как делать различные шрифты.
В этой статье будет очень много полезного, так что читайте её до конца и всё увидите.
Что нам понадобится:
Для каталога и одной категории, нам понадобятся:
- Категория(какая?).
- Продукт и его цена.
- Фото продукта.
- Описание продукта
Собираем данные
1. Категория будет напитки(просто потому что).
2. 3. 4. Описание, название и фото мы уже нашли, а цену придумаем сами.
Много брать не будем, я взял 7 напитков, чтобы просто долго не загружать их фото.
Сохраняем фото
Покажу вам более удобный вариант, что в дальнейшем нам облегчит работу.
Создаём в папке с ботом папку "capture" и в ней папку с названием "напитки".
"capture" будет выступать в роли хранилища всех фотографий каталога, а папка "напитки" в роли категории.
Теперь сохраняем фото напитков в папку "напитки" называя их "1.jpg", "2.jpg", "3.jpg" и так далее.
Потом поймёте зачем.
Главное не забудьте где какой напиток, что ,в прочем, я почти и сделал.
Поэтому сразу делаем два дела, чтобы точно не забыть где и что.
Создаём в папке с ботом файл: "drinks.py" и открываем его.
Открываем файл "drinks.py", я ,как обычно, делаю это через python IDLE.
и в нём создаём массив:
8 строк и 5 столбцов. Как в примере ниже
Вы можете подумать: "Но почему же строк 8, если напитков у нас 7?".
А всё потому, что, иногда, при вызове 0-ой строки у вас может вылетать ошибка. Чтобы этого не допускать, сразу её отбрасываем.
Для чего же 5 столбцов:
Столбец 1. Порядковый номер напитка, он может и не нужен в данном примере, т.к. категория у нас одна, но для удобства я оставлю.
Столбец 2. Цена напитка.
Столбец 3. Название напитка.
Столбец 4. Описание напитка.
Столбец 5. Путь к фото напитка.
Вот теперь сохраняем наши фотографии и заносим всё, кроме пути к фото в матрицу, должно получиться так:
Осталось внести только путь.
Т.к. папка каталога находится у нас в папке с ботом, то весь путь для неё указывать не нужно.
Добавим в наш 5-ый столбец путь ко всем напиткам,
Порядковый номер строки, точно такой же, как и номер картинки к напитку,(и номер итерации когда мы пойдём по массиву такой же) поэтому просто везде пишем "напитки/1.jpg", "напитки/2.jpg", "напитки/3.jpg" и т.д.
Я упомянул про итерацию потому, что мы можем сделать так. что везде напишем в столбцах только "напитки/", а в программе уже итерацией допишем номер фото, но я оставлю так как есть(номера пропишем сами).
Если на каком-то этапе уже что-то не понятно, или в конце поймёте или пишите ваши вопросы.
Итак, всё у нас готово и можно приступить к коду.
Приступаем к коду.
Если раньше я выкладывал весь код и только потом его объяснял, то сейчас наверное будет по частям, а в конце весь код.
Пока кнопка "🍹Напитки🍹" ничего не делает.
Я вам хочу показать два способа отображения напитков, поэтому сразу сделаем возможность выбора способа отображения(сплошняком или по отдельности)
Т.к. у нас это inline кнопка, то ответить на неё мы можем только с помощью
callback_query_handler.
1. Хэндлер на варианты отображения после нажатия кнопки "🍹Напитки🍹" (можно его не делать, если вы точно знаете что будете использовать только "сплошняком" вывод блюд или по отдельности, просто люди разные и каждому будет удобно по разному).
2. Хэндлер на вывод блюд по выбранному отображению.
Пишем хэндлеры.
@bot.callback_query_handler(func=lambda c: int(c.data)==1 or int(c.data)==2)
def callback_inline(call):
bot.answer_callback_query(callback_query_id=call.id, text="Вывожу блюда, пожалуйста, подождите.", show_alert=True)
if int(call.data)==1:
for i in range(1,8):
pricebtn = types.InlineKeyboardButton(text=str(drinks.matrix[i][1])+'₽', callback_data=i)
price = types.InlineKeyboardMarkup().add(pricebtn)
bot.send_photo(call.from_user.id, open('capture/'+str(drinks.matrix[i][4]), 'rb'), caption='<u><b><i>'+str(drinks.matrix[i][2])+"</i></b></u>\n\n<i>"+str(drinks.matrix[i][3])+"</i>",parse_mode="HTML",reply_markup=price)
Т.к. второй хэндлер не влез, закрепляю код второго хэндлера выше.
Объяснения:
НЕ ЗАБУДЬТЕ импортировать файл drinks!!!
У inline кнопки есть обязательный параметр callback_data, и у нас в кнопке к категории напитки он равен "🍹Напитки🍹", т.е. мы написали хэндлер который ловит этот callback_data, когда пользователь нажимает кнопку "🍹Напитки🍹".
Далее у нас этот хэндлер(в который зашла программа после нажатия кнопки "🍹Напитки🍹" ) выводит пользователю сообщение с вариантами отображения блюд, где в свою очередь тоже есть две кнопки с callback_data "1" и "2".
И после выбора отображения мы переходим ко второму хэндлеру который ловит оба выбора отображения, но внутри него(хэндлера) мы различаем кнопки, т.к. у нас есть возможность посмотреть какой callback_data у нас зашёл. Можете проверить и написать в хэндлере "print(call.data)" и посмотреть что выйдет в консоль, почему "call.data"?, а потому что)))
В параметре call у нас хранится всё что мы можем узнать от кнопки: информацию о пользователе, какой у неё callback_data, какой на ней был текст(может пригодиться) и другое. можете посмотреть выведя call в консоль.
Также мы будем в хэндлере использовать call.from_user.id вместо message.from_user.id.
bot.answer_callback_query(callback_query_id=call.id, text="Выберите формат отображения.", show_alert=False)
Данная строка в хэндлере отвечает пользователю в форме отображающегося окошка с кнопкой "ок", если параметр show_alert=True.
И в виде пуш уведомления сверху если show_alert=False.
Поменяйте и проверьте.
Также, делая inline кнопки я сначала создаю кнопки, а потом их уже вношу в клавиатуру с помощью параметра ".row(***)".
Параметр row() добавляет заданные кнопки в одну строку(у всего есть предел), поэтому я всегда пользуюсь им, т.к. знаю как будет выглядеть и сколько кнопок будет в строке.
Если вы хотите сделать несколько строк, то просто внесите кнопки так:
.row(button,button1).row(button2).row(button3).
Или можете воспользоваться параметром .add() написать в него кнопок сколько захотите. но по умолчанию в нём стоит две кнопки в строке, так что придётся поставить значение побольше, если надо, а как это сделать можете посмотреть в документации. Или спросить меня(не хочу на этом заострять внимание, т.к. суть статьи другая).
bot.send_photo(call.from_user.id, open('capture/'+str(drinks.matrix[i][4]), 'rb'), caption='<u><b><i>'+str(drinks.matrix[i][2])+"</i></b></u>\n\n<i>"+str(drinks.matrix[i][3])+"</i>",parse_mode="HTML",reply_markup=price)
Собственно, в этой строке у нас выводятся блюда.
Параметр 1. call.from_user.id - чат с пользователем(куда отправлять).
Параметр 2. open('capture/'+str(drinks.matrix[i][4]), 'rb') - готовит нашу картинку с напитком к отправке. Как видите путь с папки с ботом я дополняю "capture/" и соединяю его с путём который у нас находится в массиве в 4-ом столбце(столбцы начинаются с 0).
Обращаемся к массиву в файле drinks с помощью точки "drinks.matrix[*][*]".
Параметр 3. caption='<u><b><i>'+str(drinks.matrix[i][2])+"</i></b></u>\n\n<i>"+str(drinks.matrix[i][3])+"</i>" - Описание к картинке, тут я вывожу сначала название напитка,(также беру из массива) и затем через отступ вывожу описание. Как вы можете заметить тут появляются непонятные <u><b><i>, а это стили шрифта в HTML, который я объявляю далее как parse_mode="HTML", поэкспериментируйте или посмотрите документацию, что за что отвечает. Главное не забудьте что их надо, соответственно, закрывать </i></b></u>.
Также я приклепляю inline кнопку с текстом цены, которую я взял из массива.
Идём далее, Пока у нас есть только отображение сплошняком, делаем отображение по частям.
Выводить будем одно блюдо и дальше его уже листать,
Если захотите можете выводить сколько вашей душе угодно блюд, только чем больше разом будет листаться наших блюд, тем дольше они будут обновляться.
Я сделаю три хэндлера:
1. Если начало нашей категории, то надо, чтобы была в пролистывании только кнопка "Далее"
2.Если не начало и не конец, то "Далее" и "Назад".
3.Если конец категории, то только "Назад".
Чтобы у нас было "пролистывание", нам надо редактировать сообщение с напитком которое уже есть в чате на следующий напиток или на предыдущий, но как же нам быть если этого сообщения ещё нет? Писать ещё один хэндлер где будет выводиться самый первый напиток?
Я сделал, чтобы хэндлер в котором мы оказываемся после выбора отображения, сразу выводился один напиток, если мы выбрали отображение по отдельности, а уже затем переходим к трём другим хэндлерам, где всё зависит от того, начало это, середина или конец.
Строчка которая не влезла:
bot.edit_message_media(chat_id=call.from_user.id, message_id=int(call.message.message_id), media=types.InputMediaPhoto(photo, caption='<u><b><i>'+str(drinks.matrix[a][2])+'</i></b></u>\n\n<i>'+str(drinks.matrix[a][3])+"</i>",parse_mode="HTML"),reply_markup=price)
Я бы вставил сюда всё одной картинкой, но тогда шрифт будет очень мелким, да и код в виде текста тут не очень выглядит, надеюсь вам понятно.
Видео с работой бота я прикреплю в посте после этой статьи.
Из объяснений к последним хэндлерам хочется добавить, что функцию по замене картинки и текста лучше запомните, вы мало где её найдёте рабочую.
1. a=int(call.data)-10 - Здесь мы просто от полученного callback_data вычитаем 10 чтобы в дальнейшем приписать к первому индексу матрицы скромную "а".
Далее у нас идут кнопки с ценой, Далее и Назад.
В callback_data к кнопкам Назад и Далее мы передаём значение "int(call.data)+1" или "int(call.data)-1", "a+1" и "а-1" - нам не подходит, т.к. в "a" значение индекса напитка.
В строке photo = open('capture/'+str(drinks.matrix[a][4]), 'rb'), мы также открываем путь к нашей картинке, сделать это в функции редактирования картинки не удастся.
__________________________________________________________________________________________
-Спасибо, что дочитали данную статью до конца, Надеюсь, она была вам полезна. Также если что-то осталось не разобранным и непонятным - пишите в комментарии. Подписывайтесь, дальше будет ещё интереснее.