Проект "Кава"

Користувачеві пропонується обрати один чи кілька компонентів, які він полюбляє у кавовому напої: молоко, збиті вершки, молочна пінка, карамель, шоколадний сироп тощо.

Програма формує перелік напоїв, які відповідають обраним смакам. Наприклад, якщо вибрано молочну пінку, то можна запропонувати такі напої, як капучино, марочино, лате, мокко. 


Інформацію зчитуємо з файлу kava.txt:

флет вайт, молоко, еспресо
капучино, молоко, молочна пінка, еспресо
макіато, молочна пінка, еспресо
лате, молоко, молочна пінка, еспресо
кон панна, збиті вершки, еспресо
лате макіато, молоко, збиті вершки, еспресо
айріш, віскі, збиті вершки, еспресо
раф, сироп, молочна пінка, еспресо
глясе, морозиво, шоколад, еспресо
мокко, шоколад, молоко, збиті вершки, еспресо
марочино, шоколад, молочна пінка, еспресо

Спочатку в описі йде назва напою, далі - складники.

Отже, потрібно сформувати загальне меню, виділити назви напоїв та їхні рецепти (складники без назви). Також потрібно мати окремий список інгредієнтів, які б не повторювались, щоб запропонувати їх користувачеві на вибір.


Зчитуємо дані з файлу і з кожного рядка перший елемент (номер нуль) записуємо у список напоїв coffee, а решту елементів - у список рецептів recipes:

file=open('kava.txt')
coffee=[] #список напоїв
recipes=[] #список рецептів
for line in file:
    coffee.append(line.split(', ')[0])
    recipes.append(line.split(', ')[1:])


Наступний крок - сформувати список унікальних складників components. Переглядаючи усі об'єкти у списку рецептів, якщо певного компонента немає у списку components, то дописуємо його у цей список:

components=[] #список складників
for item in recipes:
    for element in item:
        if element.strip() not in components:
            components.append(element.strip())


Форму для цього проекту будемо створювати у коді, а не у дизайнері PyQt Designer.

# Імпорт необхідних модулів
import sys
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtGui import *            


class MyWindowClass(QtGui.QMainWindow):     #зверніть увагу, тут відсутній файл з інтерфейсом .ui
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent) #створення вікна
        self.resize(570, 320) #розміри вікна


# Код основної програми:
app = QtGui.QApplication(sys.argv)
myWindow = MyWindowClass(None)
myWindow.show()
app.exec_()


Розмістимо на формі (в описі класу class MyWindowClass) напис із запрошенням до вибору улюблених складників кави, перелік усіх складників у вигляді прапорців та кнопку, клацання якої переводить до наступного етапу роботи програми.


Напис на формі

Список компонентів

Кнопка


Обробка клацання кнопки "Далі"

Показ рекомендованих напоїв (на основі списку service)


Карта знань проекту

Завдання

Проект, описаний вище, може надати рекомендації, в яких один і той же напій зустрічається кілька разів. Потрібно:

1) забезпечити показ кожного напою лише 1 раз.


Формування списку напоїв зараз відбувається за принципом: перебираємо усі прапорці, і якщо якийсь із них позначений, його текст додаємо до списку service. Потрібно внести зміну: якщо прапорець позначено, то його потрібно додавати до списку не завжди, а лише в тому разі, якщо його у цьому списку ще немає. Водночас, нам буде корисно зберегти й список з дублікатами напоїв, адже якщо напій повторюється кілька разів, це означає, що він підходить замовнику за кількома складниками. На основі цих даних зможемо формувати рекомендований рейтинг напоїв у завданні 4. 

Тому після формування списку service слід на його основі сформувати список menu, і застосовувати функцію show_menu вже до цього новоствореного списку.


2) додати зображення.

Додати зображення у програму можна через елемент інтерфейсу label , зокрема властивість pixmap. Слід імпортувати методи обробки PyQt4.QtGui, та створивши об'єкт класу QPixmap, передати значення цього об'єкту в метод setPixmap. Розмір зображення задається через resize та setScaledContents.


3) відсортувати напої за пріоритетністю: якщо обраний сироп та молочна пінка, то найточніше такому запиту відповідає напій раф (у ньому є обидва цих складники), а решта: капучино, макіато, лате, марочино - мають лише один із улюблених компонентів.

.

Для формування списку рекомендованих напоїв використано список service, у який ми заносимо всі напої, які відповідають прапорцям, що позначив користувач. Назви у цьому списку можуть дублюватись. На основі цього списку ми створили список menu, в який заносили лише ті напої, які раніше не зустрічались. Тепер потрібно з'ясувати, які напої у списку service повторювались, і відповідно впорядкувати список menu за їх частотою.

Для вирішення цієї задачі можна застосувати кілька стратегій. Одна із них - створити список частот напоїв (rating) на основі списків service та menu. Спочатку список рейтингу складається з нулів (яких має бути стільки, скільки є унікальних не повторюваних напоїв). Далі, переглядаємо список service (нагадаємо, у ньому напої повторюються). Для кожного з елементів списку service шукаємо його номер у списку menu (тут напої вже не повторюються, тому кожен з них має унікальний номер), і додаємо 1 до кількості таких напоїв у списку рейтингу rating.

        rating=[0]*len(menu) # список rating містить стільки нулів, скільки позицій у списку menu
        for element in service: # для кожного напою в списку service
            n=menu.index(element) # в списку menu шукаємо номер цього напою
            rating[n]+=1 # додаємо до кількості таких напоїв одиницю

Тепер потрібно упорядкувати два списки menu та rating за значеннями зі списку rating (згадаймо, який це спосіб сортування)

        for j in range(len(rating)-1): # для кожного з елементів списку rating
           for i in range(len(rating)-1): # переглядаємо весь список
              if rating[i]<rating[i+1]: # якщо менший стоїть перед більшим
                rating[i+1], rating[i] = rating[i], rating[i+1] # поміняти місцями 2 елементи списку rating
                menu[i+1], menu[i] = menu[i], menu[i+1] # поміняти місцями 2 елементи списку menu

Таким чином ми упорядкували ці два списки за спаданням значень рейтингу напоїв. При виведенні меню функцією self.show_menu(menu) отримаємо впорядкований перелік, у якому вгорі будуть ті напої, які найкраще відповідають запитам відвідувача.


Іншою стратегією може бути, наприклад, застосування такої структури даних, як словник: https://dystosvita.gnomio.com/mod/page/view.php?id=2241


4) додати можливість обчислення суми до сплати.

Оскільки потрібно додати інформацію про вартості напоїв, варто додати цю інформацію у текстовий файл. Найпростіше буде додати ці дані одразу після назви напою, і внести зміни в цикл зчитування даних з файлу.

prices=[]  # список цін
for line in file:
    coffee.append(line.split(', ')[0])
    prices.append(line.split(', ')[1])
    recipes.append(line.split(', ')[2:])


Далі, потрібна кнопка, яка з'являтиметься після вибору напою, і виконуватиме виведення вартості замовлення. Отже, потрібно перебрати всі прапорці (у них виведені назви напоїв), і якщо прапорець позначено, то до загальної суми потрібно додати вартість цього напою, яку ми будемо визначати, орієнтуючись на його назву зі списку напоїв. Тобто, будемо шукати назву напою в списку coffee, і це означатиме, що у списку prices під тим самим номером розміщена ціна цього напою.


Проте, у попередній процедурі створення прапорців для напоїв, ми не заносили їх у список, а лише виводили на екран - тож потрібно внести зміни:

Було Стало
  # функція def show_menu(self, menu):

        n=2                         
        for element in menu:     
            tab1 = QtGui.QCheckBox(element,self)  
            tab1.move(10, 20*n)    
            n+=1                   
            tab1.show()            

        global new_options	# список прапорців
        new_options=[]		# список спочатку порожній
        n=2                         
        for element in menu:     
            tab1 = QtGui.QCheckBox(element,self)  
            tab1.move(10, 20*n)    
            n+=1                  
            tab1.show()             
            new_options.append(tab1) # додаємо прапорець до списку


Далі, створюємо власне кнопку button2 з текстом Порахувати, до якої прив'язуємо функцію calculate. Цей код потрібно розмістити у кінці функції показу списку напоїв def show_menu(self, menu):


        global button2                   # створення змінної з кнопкою
        button2=QtGui.QPushButton(self)  # створення елементу інтерфейсу PushButton, запис у змінну button
        button2.setText("Порахувати")          # задаємо текст кнопки
        button2.move(400, 200)           # розміщуємо кнопку зі зміщенням
        button2.show()                   # показ кнопки на формі
        button2.clicked.connect(self.calculate)  # створення методу обробки клацання кнопки


Опишемо функцію calculate - у ній потрібно перебирати усі прапорці з напоями, і якщо користувач позначив певний прапорець, визначаємо вартість цього напою і заносимо цю вартість у список замовлення order:

    def calculate(self):
        order=[]                   # порожній список замовлення
        for element in new_options:      # для кожного прапорця з напоями
            if element.isChecked():  # якщо прапорець позначено
                n=coffee.index(element.text()) # у змінну n записуємо номер (індекс) елемента списку coffee, що відповідає тексту позначеного прапорця
                order.append(prices[n])     # додати ціну напою зі списку price до списку замовлення
            element.hide()           # приховати прапорець
        print(order)     # друкуємо на екран список замовлення
        button2.hide()                # приховати кнопку "Порахувати"


Залишається порахувати загальну вартість замовлення: суму всіх елементи списку order. Зверніть увагу, що у ньому текстові дані, а не числа - для переведення потрібна функція int. Виводити вартість замовлення потрібно на форму в елемент Label.


5) додати можливість замовити кілька порцій напою

Наступний крок - додавання можливості обрати кількість порцій. Використаємо компонент spinBox:

        global spinBox
        spinBox=QtGui.QSpinBox(self)
        spinBox.move(300, 100)
        spinBox.show()

Значення з цього компонента зберігається у параметрі spinBox.value() - це ціле число, на яке можна виконувати множення при обчисленні загальної вартості замовлення.

Зверніть увагу, що якщо позначено кілька прапорців, слід створювати кілька компонентів spinBox.


5) додати можливість формування замовлення кількох людей з різними смаками (після опрацювання замовлення однієї людини додати можливість вибрати улюблені складники наступному клієнту).

Подумайте, яку кнопку потрібно створити, і яку функцію вона повинна викликати. Можливо, потрібно внести зміни у попередній код.


Дошка з описом змінних: https://trello.com/b/SlnNk8FB/10


Результат своєї роботи (код програми) здайте як виконане завдання https://dystosvita.gnomio.com/mod/assign/view.php?id=3648

Остання зміна: неділя 21 квітня 2019 1:10