在继续之前允许显示 kivy 弹出窗口(或进度条)
Allow kivy popup (or progress bar) to show before continuing
我和调酒师一起回来了!
所以现在一切正常,我可以控制泵,并且可以根据需要更新所有内容。现在我想在点击要喝的饮料后添加一个弹出窗口或一个进度条。
我的问题是弹出窗口window直到函数'pour_drink'完成后不久才出现。据我了解,这与在执行其余任务之前没有给弹出窗口足够的时间来显示有关。我试过 time.sleep() 但这会暂停一切,我也试过寻找其他解决方案,但无济于事。
现在的代码 不是 一个很好的代码,我使用了邪恶的全局变量,但它有效,所以我对此很满意而你没有 需要 对代码的一般结构提供反馈(尽管如果您想查看所有写得不好的代码,欢迎提供反馈)。另外,忽略所有评论..它们没有帮助,我会在完成项目之前做出更好的评论。
函数pour_drink在classDrinkMenu中,函数popup_func 用于弹出窗口 window。因此,如果有人有一个好的(或丑陋的)解决方案,我会洗耳恭听。这个项目占用了学校工作的时间,我很笨所以允许它。
编辑:澄清:我想添加一个弹出窗口window(或进度条),最好有一个取消按钮来取消倒酒。
class drinkMenu 中的 'pour_drink' 处理用于倾倒饮料的泵的函数。所以我想要的是弹出窗口 window 在访问倒函数时显示,然后在完成后消失。
并且如果可能的话最好有一个取消按钮让程序从函数中跳出pour_drink,但是如果实现起来太困难就没有这个必要了
到目前为止,我已经尝试过多处理,但我无法让它工作。 drinkMenu class 中的 popup_func 来自于我尝试拆分两个函数 pour_drink 和 popup_func分为两个进程。但问题仍然存在,弹出窗口 window 仅在 pour_drink 函数完成后短暂显示。
感谢您的帮助!
.py 文件:
#Necessary files: bartender.py, bartenderkv.kv pump_config.py, drinks_doc.py
from kivy.core.window import Window
#Uncomment to go directly to fullscreen
Window.fullscreen = 'auto'
#Everything needed for kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.clock import mainthread
from functools import partial
from kivy.uix.popup import Popup
from kivy.uix.label import Label
#Import drink list
from drinks_doc import drink_list, drink_options
from pump_config import config
pins_ingredients = {}
#import gpio control library
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
import time
#Define the different screens
class MainMenu(Screen):
def use_last(self,*args,**kwargs):
global pins_ingredients
#Write a function that use the last used settings,
#i.e that the pumps have corresponding ingredients.
try:
from pump_config import config
for pmp in config:
pins_ingredients[config[str(pmp)]['value']] = [config[str(pmp)]['pin'],config[str(pmp)]['flowrate']]
self.load_available_drinks(pins_ingredients.keys())
self.parent.current = "DrinkMenu"
except:
bttn = Button(text = "Close", font_size=24, size_hint_x = 0.5, size_hint_y = 0.5)
self.popup = Popup(title='No Previous Settings Found!', title_size = '32sp',
content=bttn,
auto_dismiss=False)
bttn.bind(on_release = self.close_n_open)
self.popup.open()
def close_n_open(self,*args,**kwargs):
self.popup.dismiss()
def load_available_drinks(self, ingredients_list):
global drinks
drinks = []
for dri in drink_list:
dr = list(dri["ingredients"].keys())
result = all(elem in ingredients_list for elem in dr)
if result:
drinks.append(dri["name"])
class DrinkMenu(Screen):
@mainthread
def on_enter(self):
self.buttons = []
self.ids.drinks.clear_widgets()
for btn in range(len(drinks)):
self.buttons.append(Button(text=str(drinks[btn])))
self.buttons[btn].bind(on_press = self.pour_drink)
self.ids.drinks.add_widget(self.buttons[btn])
def popup_func(self, onoff):
if onoff == "ON":
self.popup = Popup(title='No Previous Settings Found!', title_size = '32sp',
auto_dismiss=False)
self.popup.open()
elif onoff == "OFF":
self.popup.dismiss()
for k in range(10):
pass
def pour_drink(self, button):
self.popup_func("ON")
#The pins_ingredient dictionary, each value has a list, first number is pin on raspberry pi, the second is the flowrate in ml/min =>
#temp_ingredients = drink_list[button.text]
for num in range(len(drink_list)):
if drink_list[num]['name'] == str(button.text):
temp_ingredients = drink_list[num]['ingredients']
for ing in temp_ingredients:
flow_rate = pins_ingredients[ing][1]
amount = temp_ingredients[ing]
temp_ingredients[ing] = amount/(flow_rate/60)
continue
#The pins_ingredients has ingredients as keys, list as value
#with first pin on RPi, second flowrate of that pump
#The temp_ingredients has ingredients as keys, and a list as
#value, first: how long the pump needs to be on for,
#second is primed for "ON"
ings = len(list(temp_ingredients.keys()))
#print(temp_ingredients)
start = time.time()
state = True
for ing in temp_ingredients:
GPIO.output(pins_ingredients[str(ing)][0],GPIO.HIGH)
deleted = []
while state:
for ing in temp_ingredients:
if ing not in deleted:
if time.time()-start >= temp_ingredients[ing]:
#print(temp_ingredients[ing])
GPIO.output(pins_ingredients[str(ing)][0],GPIO.LOW)
ings -= 1
deleted.append(ing)
if ings == 0:
state = False
self.popup_func("OFF")
class TopUpMenu(Screen):
global pins_ingredients
@mainthread
def on_enter(self):
global pins_ingredients
#print(pins_ingredients)
listed = list(pins_ingredients.keys())
self.buttons = []
self.ids.topupdrinks.clear_widgets()
counter = 0
for btn in range(len(listed)):
if str(listed[btn]) != "None":
self.buttons.append(Button(text=str(listed[btn])))
self.buttons[counter].bind(on_press = partial(self.top_up, str(listed[btn]), "ON"))
self.buttons[counter].bind(on_release = partial(self.top_up,str(listed[btn]),"OFF"))
self.ids.topupdrinks.add_widget(self.buttons[counter])
counter += 1
def top_up(self, *args, **kwargs):
global pins_ingredients
#If state is "ON" set pump pin high
#If state is "OFF" set pump pin low
ingredient = str(args[0])
state = str(args[1])
if state == "ON":
#Set the pin high to turn on corresponding pump
#print(str(ingredient)+ " ON")
GPIO.output(pins_ingredients[ingredient][0],GPIO.HIGH)
if state == "OFF":
#Set the pin low to turn off corresponding pump
#print(str(ingredient)+" OFF")
GPIO.output(pins_ingredients[ingredient][0],GPIO.LOW)
class LoadNewIngredients(Screen):
global drinks
global ingredients
global pins_ingredients
#I want to use the ingredients list in the kivy file
temp_list = [None]*len(list(config.keys()))
def anydup(self, thelist):
seen = set()
for x in thelist:
if x != None:
if x in seen: return True
seen.add(x)
return False
def get_ingredients(self,*args,**kwargs):
global ingredients
ingredients = []
for drink in range(len(drink_list)):
ings = list(drink_list[drink]['ingredients'].keys())
for ing in range(len(ings)):
elem = ings[ing]
if elem not in ingredients:
ingredients.append(elem)
return ingredients
def spinner_clicked(self, ident, value):
if ident == 1:
self.temp_list[0] = str(value)
if ident == 2:
self.temp_list[1] = str(value)
if ident == 3:
self.temp_list[2] = str(value)
if ident == 4:
self.temp_list[3] = str(value)
if ident == 5:
self.temp_list[4] = str(value)
if ident == 6:
self.temp_list[5] = str(value)
if ident == 7:
self.temp_list[6] = str(value)
if ident == 8:
self.temp_list[7] = str(value)
if ident == 9:
self.temp_list[8] = str(value)
def continued(self,*args,**kwargs):
global pins_ingredients
if self.temp_list[0] != "Pump_1":
config["pump_1"]["value"] = self.temp_list[0]
else:
config["pump_1"]["value"] = None
if self.temp_list[1] != "Pump_2":
config["pump_2"]["value"] = self.temp_list[1]
else:
config["pump_2"]["value"] = None
if self.temp_list[2] != "Pump_3":
config["pump_3"]["value"] = self.temp_list[2]
else:
config["pump_3"]["value"] = None
if self.temp_list[3] != "Pump_4":
config["pump_4"]["value"] = self.temp_list[3]
else:
config["pump_4"]["value"] = None
if self.temp_list[4] != "Pump_5":
config["pump_5"]["value"] = self.temp_list[4]
else:
config["pump_5"]["value"] = None
if self.temp_list[5] != "Pump_6":
config["pump_6"]["value"] = self.temp_list[5]
else:
config["pump_6"]["value"] = None
if self.temp_list[6] != "Pump_7":
config["pump_7"]["value"] = self.temp_list[6]
else:
config["pump_7"]["value"] = None
if self.temp_list[7] != "Pump_8":
config["pump_8"]["value"] = self.temp_list[7]
else:
config["pump_8"]["value"] = None
if self.temp_list[8] != "Pump_8":
config["pump_9"]["value"] = self.temp_list[8]
else:
config["pump_9"]["value"] = None
if not self.anydup(self.temp_list):
#print(self.temp_list)
pins_ingredients = {}
filehandler = open('pump_config.py', 'wt')
filehandler.write("config = " + str(config))
filehandler.close()
for pmp in config:
if config[str(pmp)]['value'] != None:
pins_ingredients[config[str(pmp)]['value']] = [config[str(pmp)]['pin'],config[str(pmp)]['flowrate']]
#print(pins_ingredients)
self.load_available_drinks(pins_ingredients.keys())
self.ids.spinner_id_1.text = "Pump_1"
self.ids.spinner_id_2.text = "Pump_2"
self.ids.spinner_id_3.text = "Pump_3"
self.ids.spinner_id_4.text = "Pump_4"
self.ids.spinner_id_5.text = "Pump_5"
self.ids.spinner_id_6.text = "Pump_6"
self.ids.spinner_id_7.text = "Pump_7"
self.ids.spinner_id_8.text = "Pump_8"
self.ids.spinner_id_8.text = "Pump_9"
self.parent.current = "DrinkMenu"
else:
bttn = Button(text = "Ok, I'm sorry!", font_size=24, size_hint_x = 0.5, size_hint_y = 0.5)
self.popup = Popup(title='There can be NO duplicate ingredients!', title_size = '32sp',
content=bttn,
auto_dismiss=False)
bttn.bind(on_release = self.close_n_open)
self.popup.open()
self.parent.current = "LoadNewIngredients"
def close_n_open(self,*args,**kwargs):
self.popup.dismiss()
def load_available_drinks(self, ingredients_list):
global drinks
drinks = []
for dri in drink_list:
dr = list(dri["ingredients"].keys())
result = all(elem in ingredients_list for elem in dr)
if result:
drinks.append(dri["name"])
#Define the ScreenManager
class MenuManager(ScreenManager):
pass
#Designate the .kv design file
kv = Builder.load_file('bartenderkv.kv')
class BartenderApp(App):
def build(self):
return kv
if __name__ == '__main__':
#set up all gpio pins
for pmp in config:
GPIO.setup(config[pmp]['pin'],GPIO.OUT)
GPIO.output(config[pmp]['pin'],GPIO.LOW)
BartenderApp().run()
为了完整性:.kv 文件:
#Need to define everything, the ScreenManager is the entity that keeps tabs
#on all the different menu windows
#This is for the popup, lets you instansiate a class from anywhere
#:import Factory kivy.factory.Factory
#:import ScrollView kivy.uix.scrollview
MenuManager:
MainMenu:
LoadNewIngredients:
DrinkMenu:
TopUpMenu:
<MainMenu>:
#name defines the signature, referenced when we want to move to it
name: "MainMenu"
GridLayout:
rows: 3
size: root.width, root.height
padding: 10
spacing: 10
Label:
text: "Main Menu"
font_size: 32
GridLayout:
cols: 2
size: root.width, root.height
spacing: 10
Button:
text: "Use Last Settings"
font_size: 32
on_press: root.use_last()
#on_release: app.root.current = "DrinkMenu"
Button:
text: "Load New Ingredients"
font_size: 32
on_release: app.root.current = "LoadNewIngredients"
Button:
text: "See Permissable Ingredients"
font_size: 32
#on_press: print("It Works")
on_release: Factory.PermissablePopup().open()
<LoadNewIngredients>:
name: "LoadNewIngredients"
GridLayout:
cols: 2
size: root.width, root.height
padding: 10
spacing: 10
#size hint sets relative sized, x-dir, y-dir
GridLayout:
size: root.width, root.height
size_hint_x: 0.2
rows: 2
Button:
text: "Continue"
font_size: 24
on_release: root.continued()
Button:
text: "Main Menu"
font_size: 24
on_release: app.root.current = "MainMenu"
GridLayout:
id: choices
rows: 3
cols: 6
orientation: 'tb-lr'
Label:
id: pump_1
text: "Pump 1"
font_size: 24
Label:
id: pump_2
text: "Pump 2"
font_size: 24
Label:
id: pump_3
text: "Pump 3"
font_size: 24
#Spinner is the easy drop down version in kivy, lets see how it looks.
Spinner:
id: spinner_id_1
text: "Pump_1"
#This references the get_ingredients function in the main p
values: root.get_ingredients()
on_text: root.spinner_clicked(1,spinner_id_1.text)
Spinner:
id: spinner_id_2
text: "Pump_2"
values: root.get_ingredients()
on_text: root.spinner_clicked(2,spinner_id_2.text)
Spinner:
id: spinner_id_3
text: "Pump_3"
values: root.get_ingredients()
on_text: root.spinner_clicked(3,spinner_id_3.text)
Label:
id: pump_4
text: "Pump 4"
font_size: 24
Label:
id: pump_5
text: "Pump 5"
font_size: 24
Label:
id: pump_6
text: "Pump 6"
font_size: 24
Spinner:
id: spinner_id_4
text: "Pump_4"
values: root.get_ingredients()
on_text: root.spinner_clicked(4,spinner_id_4.text)
#Spinner is the drop down version, lets see how it looks.
Spinner:
id: spinner_id_5
text: "Pump_5"
values: root.get_ingredients()
on_text: root.spinner_clicked(5,spinner_id_5.text)
Spinner:
id: spinner_id_6
text: "Pump_6"
values: root.get_ingredients()
on_text: root.spinner_clicked(6,spinner_id_6.text)
Label:
id: pump_7
text: "Pump 7"
font_size: 24
Label:
id: pump_8
text: "Pump 8"
font_size: 24
Label:
id: pump_9
text: "Pump 9"
font_size: 24
Spinner:
id: spinner_id_7
text: "Pump_7"
values: root.get_ingredients()
on_text: root.spinner_clicked(7,spinner_id_7.text)
Spinner:
id: spinner_id_8
text: "Pump_8"
values: root.get_ingredients()
on_text: root.spinner_clicked(8,spinner_id_8.text)
Spinner:
id: spinner_id_9
text: "Pump_9"
values: root.get_ingredients()
on_text: root.spinner_clicked(9,spinner_id_9.text)
<DrinkMenu>:
name: "DrinkMenu"
GridLayout:
cols: 2
width: root.width
height: self.minimum_height
padding: 10
spacing: 10
GridLayout:
height: root.height
size_hint_x: 0.4
rows: 2
Button:
text: "Top Up"
font_size: 24
on_release: app.root.current = "TopUpMenu"
Button:
text: "Main Menu"
font_size: 24
on_release: app.root.current = "MainMenu"
ScrollView:
size_hint_y: 0.1
pos_hint: {'x':0, 'y': 0.11}
do_scroll_x: False
do_scroll_y: True
GridLayout:
id: drinks
orientation: 'lr-tb'
size_hint_y: None
size_hint_x: 1.0
cols: 3
height: self.minimum_height
#row_default_height changes the buttons height
row_default_height: 150
row_force_default: True
<TopUpMenu>:
name: "TopUpMenu"
GridLayout:
cols: 2
width: root.width
height: self.minimum_height
padding: 10
spacing: 10
GridLayout:
height: root.height
size_hint_x: 0.4
rows: 1
Button:
text: "Back"
font_size: 24
on_release: app.root.current = "DrinkMenu"
ScrollView:
size_hint_y: 0.1
pos_hint: {'x':0, 'y': 0.11}
do_scroll_x: False
do_scroll_y: True
GridLayout:
id: topupdrinks
orientation: 'lr-tb'
size_hint_y: None
size_hint_x: 1.0
cols: 3
height: self.minimum_height
#row_default_height changes the buttons height
row_default_height: 150
row_force_default: True
#Create a rounded button, the @Button is what it inherits
<RoundedButton@Button>
background_color: (0,0,0,0)
background_normal: ''
canvas.before:
Color:
rgba:
(48/255,84/255,150/255,1)\
if self.state == 'normal' else (0.6,0.6,1,1) # Color is red if button is not pressed, otherwise color is green
RoundedRectangle:
size: self.size
pos: self.pos
radius: [58]
#Create a home button
#Create a drink button
#Create a new ingredients button
#Create a use last settings button
#Create a permissable ingredients button
<PermissablePopup@Popup>
auto_dismiss: False
#size_hint: 0.6,0.2
#pos_hint: {"x":0.2, "top":0.9}
title: "Permissable Ingredients"
GridLayout:
rows: 2
size: root.width, root.height
spacing: 10
GridLayout:
cols: 2
Label:
text: "Sodas"
font_size: 32
#Add list of sodas
Label:
text: "Alcohol"
font_size: 32
#Add list of alcohols
Button:
text: "Done"
font_size: 24
on_release: root.dismiss()
您可以使用装饰器来显示弹出窗口,然后 运行 线程中的函数。这是一个独立的示例(尝试 运行 自己查看输出):
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.properties import BooleanProperty
import threading
from time import sleep
def show_popup(function):
def wrap(app, *args, **kwargs):
popup = CustomPopup() # Instantiate CustomPopup (could add some kwargs if you wish)
app.done = False # Reset the app.done BooleanProperty
app.bind(done=popup.dismiss) # When app.done is set to True, then popup.dismiss is fired
popup.open() # Show popup
t = threading.Thread(target=function, args=[app, popup, *args], kwargs=kwargs) # Create thread
t.start() # Start thread
return t
return wrap
class CustomPopup(Popup):
pass
kv = Builder.load_string( # Generic kv stuff
"""
<CustomPopup>:
size_hint: .8, .4
auto_dismiss: False
progress: 0
text: ''
title: "Drink Progress"
BoxLayout:
orientation: 'vertical'
Label:
text: root.text
size_hint: 1, 0.8
ProgressBar:
value: root.progress
FloatLayout:
Button:
text: 'Pour me a Drink!'
on_release: app.mix_drinks()
"""
)
class MyMainApp(App):
done = BooleanProperty(False)
def build(self):
return kv
@show_popup
def mix_drinks(self, popup): # Decorate function to show popup and run the code below in a thread
popup.text = 'Slicing limes...'
sleep(1)
popup.progress = 20
popup.text = 'Muddling sugar...' # Changing the popup attributes as the function runs!
sleep(2)
popup.progress = 50
popup.text = 'Pouring rum...'
sleep(2)
popup.progress = 80
popup.text = 'Adding ice...'
sleep(1)
popup.progress = 100
popup.text = 'Done!'
sleep(0.5)
self.done = True
if __name__ == '__main__':
MyMainApp().run()
在更新 CustomPopup
实例中的进度条和标签时,App.mix_drinks
在线程中运行 运行s。
添加取消按钮是另一种怪兽。如果你真的想这样做,你可以终止线程(参见 Is there any way to kill a Thread?),但我想不出一个简单的方法来做到这一点。
我和调酒师一起回来了!
所以现在一切正常,我可以控制泵,并且可以根据需要更新所有内容。现在我想在点击要喝的饮料后添加一个弹出窗口或一个进度条。
我的问题是弹出窗口window直到函数'pour_drink'完成后不久才出现。据我了解,这与在执行其余任务之前没有给弹出窗口足够的时间来显示有关。我试过 time.sleep() 但这会暂停一切,我也试过寻找其他解决方案,但无济于事。
现在的代码 不是 一个很好的代码,我使用了邪恶的全局变量,但它有效,所以我对此很满意而你没有 需要 对代码的一般结构提供反馈(尽管如果您想查看所有写得不好的代码,欢迎提供反馈)。另外,忽略所有评论..它们没有帮助,我会在完成项目之前做出更好的评论。
函数pour_drink在classDrinkMenu中,函数popup_func 用于弹出窗口 window。因此,如果有人有一个好的(或丑陋的)解决方案,我会洗耳恭听。这个项目占用了学校工作的时间,我很笨所以允许它。
编辑:澄清:我想添加一个弹出窗口window(或进度条),最好有一个取消按钮来取消倒酒。
class drinkMenu 中的 'pour_drink' 处理用于倾倒饮料的泵的函数。所以我想要的是弹出窗口 window 在访问倒函数时显示,然后在完成后消失。
并且如果可能的话最好有一个取消按钮让程序从函数中跳出pour_drink,但是如果实现起来太困难就没有这个必要了
到目前为止,我已经尝试过多处理,但我无法让它工作。 drinkMenu class 中的 popup_func 来自于我尝试拆分两个函数 pour_drink 和 popup_func分为两个进程。但问题仍然存在,弹出窗口 window 仅在 pour_drink 函数完成后短暂显示。
感谢您的帮助!
.py 文件:
#Necessary files: bartender.py, bartenderkv.kv pump_config.py, drinks_doc.py
from kivy.core.window import Window
#Uncomment to go directly to fullscreen
Window.fullscreen = 'auto'
#Everything needed for kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.clock import mainthread
from functools import partial
from kivy.uix.popup import Popup
from kivy.uix.label import Label
#Import drink list
from drinks_doc import drink_list, drink_options
from pump_config import config
pins_ingredients = {}
#import gpio control library
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
import time
#Define the different screens
class MainMenu(Screen):
def use_last(self,*args,**kwargs):
global pins_ingredients
#Write a function that use the last used settings,
#i.e that the pumps have corresponding ingredients.
try:
from pump_config import config
for pmp in config:
pins_ingredients[config[str(pmp)]['value']] = [config[str(pmp)]['pin'],config[str(pmp)]['flowrate']]
self.load_available_drinks(pins_ingredients.keys())
self.parent.current = "DrinkMenu"
except:
bttn = Button(text = "Close", font_size=24, size_hint_x = 0.5, size_hint_y = 0.5)
self.popup = Popup(title='No Previous Settings Found!', title_size = '32sp',
content=bttn,
auto_dismiss=False)
bttn.bind(on_release = self.close_n_open)
self.popup.open()
def close_n_open(self,*args,**kwargs):
self.popup.dismiss()
def load_available_drinks(self, ingredients_list):
global drinks
drinks = []
for dri in drink_list:
dr = list(dri["ingredients"].keys())
result = all(elem in ingredients_list for elem in dr)
if result:
drinks.append(dri["name"])
class DrinkMenu(Screen):
@mainthread
def on_enter(self):
self.buttons = []
self.ids.drinks.clear_widgets()
for btn in range(len(drinks)):
self.buttons.append(Button(text=str(drinks[btn])))
self.buttons[btn].bind(on_press = self.pour_drink)
self.ids.drinks.add_widget(self.buttons[btn])
def popup_func(self, onoff):
if onoff == "ON":
self.popup = Popup(title='No Previous Settings Found!', title_size = '32sp',
auto_dismiss=False)
self.popup.open()
elif onoff == "OFF":
self.popup.dismiss()
for k in range(10):
pass
def pour_drink(self, button):
self.popup_func("ON")
#The pins_ingredient dictionary, each value has a list, first number is pin on raspberry pi, the second is the flowrate in ml/min =>
#temp_ingredients = drink_list[button.text]
for num in range(len(drink_list)):
if drink_list[num]['name'] == str(button.text):
temp_ingredients = drink_list[num]['ingredients']
for ing in temp_ingredients:
flow_rate = pins_ingredients[ing][1]
amount = temp_ingredients[ing]
temp_ingredients[ing] = amount/(flow_rate/60)
continue
#The pins_ingredients has ingredients as keys, list as value
#with first pin on RPi, second flowrate of that pump
#The temp_ingredients has ingredients as keys, and a list as
#value, first: how long the pump needs to be on for,
#second is primed for "ON"
ings = len(list(temp_ingredients.keys()))
#print(temp_ingredients)
start = time.time()
state = True
for ing in temp_ingredients:
GPIO.output(pins_ingredients[str(ing)][0],GPIO.HIGH)
deleted = []
while state:
for ing in temp_ingredients:
if ing not in deleted:
if time.time()-start >= temp_ingredients[ing]:
#print(temp_ingredients[ing])
GPIO.output(pins_ingredients[str(ing)][0],GPIO.LOW)
ings -= 1
deleted.append(ing)
if ings == 0:
state = False
self.popup_func("OFF")
class TopUpMenu(Screen):
global pins_ingredients
@mainthread
def on_enter(self):
global pins_ingredients
#print(pins_ingredients)
listed = list(pins_ingredients.keys())
self.buttons = []
self.ids.topupdrinks.clear_widgets()
counter = 0
for btn in range(len(listed)):
if str(listed[btn]) != "None":
self.buttons.append(Button(text=str(listed[btn])))
self.buttons[counter].bind(on_press = partial(self.top_up, str(listed[btn]), "ON"))
self.buttons[counter].bind(on_release = partial(self.top_up,str(listed[btn]),"OFF"))
self.ids.topupdrinks.add_widget(self.buttons[counter])
counter += 1
def top_up(self, *args, **kwargs):
global pins_ingredients
#If state is "ON" set pump pin high
#If state is "OFF" set pump pin low
ingredient = str(args[0])
state = str(args[1])
if state == "ON":
#Set the pin high to turn on corresponding pump
#print(str(ingredient)+ " ON")
GPIO.output(pins_ingredients[ingredient][0],GPIO.HIGH)
if state == "OFF":
#Set the pin low to turn off corresponding pump
#print(str(ingredient)+" OFF")
GPIO.output(pins_ingredients[ingredient][0],GPIO.LOW)
class LoadNewIngredients(Screen):
global drinks
global ingredients
global pins_ingredients
#I want to use the ingredients list in the kivy file
temp_list = [None]*len(list(config.keys()))
def anydup(self, thelist):
seen = set()
for x in thelist:
if x != None:
if x in seen: return True
seen.add(x)
return False
def get_ingredients(self,*args,**kwargs):
global ingredients
ingredients = []
for drink in range(len(drink_list)):
ings = list(drink_list[drink]['ingredients'].keys())
for ing in range(len(ings)):
elem = ings[ing]
if elem not in ingredients:
ingredients.append(elem)
return ingredients
def spinner_clicked(self, ident, value):
if ident == 1:
self.temp_list[0] = str(value)
if ident == 2:
self.temp_list[1] = str(value)
if ident == 3:
self.temp_list[2] = str(value)
if ident == 4:
self.temp_list[3] = str(value)
if ident == 5:
self.temp_list[4] = str(value)
if ident == 6:
self.temp_list[5] = str(value)
if ident == 7:
self.temp_list[6] = str(value)
if ident == 8:
self.temp_list[7] = str(value)
if ident == 9:
self.temp_list[8] = str(value)
def continued(self,*args,**kwargs):
global pins_ingredients
if self.temp_list[0] != "Pump_1":
config["pump_1"]["value"] = self.temp_list[0]
else:
config["pump_1"]["value"] = None
if self.temp_list[1] != "Pump_2":
config["pump_2"]["value"] = self.temp_list[1]
else:
config["pump_2"]["value"] = None
if self.temp_list[2] != "Pump_3":
config["pump_3"]["value"] = self.temp_list[2]
else:
config["pump_3"]["value"] = None
if self.temp_list[3] != "Pump_4":
config["pump_4"]["value"] = self.temp_list[3]
else:
config["pump_4"]["value"] = None
if self.temp_list[4] != "Pump_5":
config["pump_5"]["value"] = self.temp_list[4]
else:
config["pump_5"]["value"] = None
if self.temp_list[5] != "Pump_6":
config["pump_6"]["value"] = self.temp_list[5]
else:
config["pump_6"]["value"] = None
if self.temp_list[6] != "Pump_7":
config["pump_7"]["value"] = self.temp_list[6]
else:
config["pump_7"]["value"] = None
if self.temp_list[7] != "Pump_8":
config["pump_8"]["value"] = self.temp_list[7]
else:
config["pump_8"]["value"] = None
if self.temp_list[8] != "Pump_8":
config["pump_9"]["value"] = self.temp_list[8]
else:
config["pump_9"]["value"] = None
if not self.anydup(self.temp_list):
#print(self.temp_list)
pins_ingredients = {}
filehandler = open('pump_config.py', 'wt')
filehandler.write("config = " + str(config))
filehandler.close()
for pmp in config:
if config[str(pmp)]['value'] != None:
pins_ingredients[config[str(pmp)]['value']] = [config[str(pmp)]['pin'],config[str(pmp)]['flowrate']]
#print(pins_ingredients)
self.load_available_drinks(pins_ingredients.keys())
self.ids.spinner_id_1.text = "Pump_1"
self.ids.spinner_id_2.text = "Pump_2"
self.ids.spinner_id_3.text = "Pump_3"
self.ids.spinner_id_4.text = "Pump_4"
self.ids.spinner_id_5.text = "Pump_5"
self.ids.spinner_id_6.text = "Pump_6"
self.ids.spinner_id_7.text = "Pump_7"
self.ids.spinner_id_8.text = "Pump_8"
self.ids.spinner_id_8.text = "Pump_9"
self.parent.current = "DrinkMenu"
else:
bttn = Button(text = "Ok, I'm sorry!", font_size=24, size_hint_x = 0.5, size_hint_y = 0.5)
self.popup = Popup(title='There can be NO duplicate ingredients!', title_size = '32sp',
content=bttn,
auto_dismiss=False)
bttn.bind(on_release = self.close_n_open)
self.popup.open()
self.parent.current = "LoadNewIngredients"
def close_n_open(self,*args,**kwargs):
self.popup.dismiss()
def load_available_drinks(self, ingredients_list):
global drinks
drinks = []
for dri in drink_list:
dr = list(dri["ingredients"].keys())
result = all(elem in ingredients_list for elem in dr)
if result:
drinks.append(dri["name"])
#Define the ScreenManager
class MenuManager(ScreenManager):
pass
#Designate the .kv design file
kv = Builder.load_file('bartenderkv.kv')
class BartenderApp(App):
def build(self):
return kv
if __name__ == '__main__':
#set up all gpio pins
for pmp in config:
GPIO.setup(config[pmp]['pin'],GPIO.OUT)
GPIO.output(config[pmp]['pin'],GPIO.LOW)
BartenderApp().run()
为了完整性:.kv 文件:
#Need to define everything, the ScreenManager is the entity that keeps tabs
#on all the different menu windows
#This is for the popup, lets you instansiate a class from anywhere
#:import Factory kivy.factory.Factory
#:import ScrollView kivy.uix.scrollview
MenuManager:
MainMenu:
LoadNewIngredients:
DrinkMenu:
TopUpMenu:
<MainMenu>:
#name defines the signature, referenced when we want to move to it
name: "MainMenu"
GridLayout:
rows: 3
size: root.width, root.height
padding: 10
spacing: 10
Label:
text: "Main Menu"
font_size: 32
GridLayout:
cols: 2
size: root.width, root.height
spacing: 10
Button:
text: "Use Last Settings"
font_size: 32
on_press: root.use_last()
#on_release: app.root.current = "DrinkMenu"
Button:
text: "Load New Ingredients"
font_size: 32
on_release: app.root.current = "LoadNewIngredients"
Button:
text: "See Permissable Ingredients"
font_size: 32
#on_press: print("It Works")
on_release: Factory.PermissablePopup().open()
<LoadNewIngredients>:
name: "LoadNewIngredients"
GridLayout:
cols: 2
size: root.width, root.height
padding: 10
spacing: 10
#size hint sets relative sized, x-dir, y-dir
GridLayout:
size: root.width, root.height
size_hint_x: 0.2
rows: 2
Button:
text: "Continue"
font_size: 24
on_release: root.continued()
Button:
text: "Main Menu"
font_size: 24
on_release: app.root.current = "MainMenu"
GridLayout:
id: choices
rows: 3
cols: 6
orientation: 'tb-lr'
Label:
id: pump_1
text: "Pump 1"
font_size: 24
Label:
id: pump_2
text: "Pump 2"
font_size: 24
Label:
id: pump_3
text: "Pump 3"
font_size: 24
#Spinner is the easy drop down version in kivy, lets see how it looks.
Spinner:
id: spinner_id_1
text: "Pump_1"
#This references the get_ingredients function in the main p
values: root.get_ingredients()
on_text: root.spinner_clicked(1,spinner_id_1.text)
Spinner:
id: spinner_id_2
text: "Pump_2"
values: root.get_ingredients()
on_text: root.spinner_clicked(2,spinner_id_2.text)
Spinner:
id: spinner_id_3
text: "Pump_3"
values: root.get_ingredients()
on_text: root.spinner_clicked(3,spinner_id_3.text)
Label:
id: pump_4
text: "Pump 4"
font_size: 24
Label:
id: pump_5
text: "Pump 5"
font_size: 24
Label:
id: pump_6
text: "Pump 6"
font_size: 24
Spinner:
id: spinner_id_4
text: "Pump_4"
values: root.get_ingredients()
on_text: root.spinner_clicked(4,spinner_id_4.text)
#Spinner is the drop down version, lets see how it looks.
Spinner:
id: spinner_id_5
text: "Pump_5"
values: root.get_ingredients()
on_text: root.spinner_clicked(5,spinner_id_5.text)
Spinner:
id: spinner_id_6
text: "Pump_6"
values: root.get_ingredients()
on_text: root.spinner_clicked(6,spinner_id_6.text)
Label:
id: pump_7
text: "Pump 7"
font_size: 24
Label:
id: pump_8
text: "Pump 8"
font_size: 24
Label:
id: pump_9
text: "Pump 9"
font_size: 24
Spinner:
id: spinner_id_7
text: "Pump_7"
values: root.get_ingredients()
on_text: root.spinner_clicked(7,spinner_id_7.text)
Spinner:
id: spinner_id_8
text: "Pump_8"
values: root.get_ingredients()
on_text: root.spinner_clicked(8,spinner_id_8.text)
Spinner:
id: spinner_id_9
text: "Pump_9"
values: root.get_ingredients()
on_text: root.spinner_clicked(9,spinner_id_9.text)
<DrinkMenu>:
name: "DrinkMenu"
GridLayout:
cols: 2
width: root.width
height: self.minimum_height
padding: 10
spacing: 10
GridLayout:
height: root.height
size_hint_x: 0.4
rows: 2
Button:
text: "Top Up"
font_size: 24
on_release: app.root.current = "TopUpMenu"
Button:
text: "Main Menu"
font_size: 24
on_release: app.root.current = "MainMenu"
ScrollView:
size_hint_y: 0.1
pos_hint: {'x':0, 'y': 0.11}
do_scroll_x: False
do_scroll_y: True
GridLayout:
id: drinks
orientation: 'lr-tb'
size_hint_y: None
size_hint_x: 1.0
cols: 3
height: self.minimum_height
#row_default_height changes the buttons height
row_default_height: 150
row_force_default: True
<TopUpMenu>:
name: "TopUpMenu"
GridLayout:
cols: 2
width: root.width
height: self.minimum_height
padding: 10
spacing: 10
GridLayout:
height: root.height
size_hint_x: 0.4
rows: 1
Button:
text: "Back"
font_size: 24
on_release: app.root.current = "DrinkMenu"
ScrollView:
size_hint_y: 0.1
pos_hint: {'x':0, 'y': 0.11}
do_scroll_x: False
do_scroll_y: True
GridLayout:
id: topupdrinks
orientation: 'lr-tb'
size_hint_y: None
size_hint_x: 1.0
cols: 3
height: self.minimum_height
#row_default_height changes the buttons height
row_default_height: 150
row_force_default: True
#Create a rounded button, the @Button is what it inherits
<RoundedButton@Button>
background_color: (0,0,0,0)
background_normal: ''
canvas.before:
Color:
rgba:
(48/255,84/255,150/255,1)\
if self.state == 'normal' else (0.6,0.6,1,1) # Color is red if button is not pressed, otherwise color is green
RoundedRectangle:
size: self.size
pos: self.pos
radius: [58]
#Create a home button
#Create a drink button
#Create a new ingredients button
#Create a use last settings button
#Create a permissable ingredients button
<PermissablePopup@Popup>
auto_dismiss: False
#size_hint: 0.6,0.2
#pos_hint: {"x":0.2, "top":0.9}
title: "Permissable Ingredients"
GridLayout:
rows: 2
size: root.width, root.height
spacing: 10
GridLayout:
cols: 2
Label:
text: "Sodas"
font_size: 32
#Add list of sodas
Label:
text: "Alcohol"
font_size: 32
#Add list of alcohols
Button:
text: "Done"
font_size: 24
on_release: root.dismiss()
您可以使用装饰器来显示弹出窗口,然后 运行 线程中的函数。这是一个独立的示例(尝试 运行 自己查看输出):
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.properties import BooleanProperty
import threading
from time import sleep
def show_popup(function):
def wrap(app, *args, **kwargs):
popup = CustomPopup() # Instantiate CustomPopup (could add some kwargs if you wish)
app.done = False # Reset the app.done BooleanProperty
app.bind(done=popup.dismiss) # When app.done is set to True, then popup.dismiss is fired
popup.open() # Show popup
t = threading.Thread(target=function, args=[app, popup, *args], kwargs=kwargs) # Create thread
t.start() # Start thread
return t
return wrap
class CustomPopup(Popup):
pass
kv = Builder.load_string( # Generic kv stuff
"""
<CustomPopup>:
size_hint: .8, .4
auto_dismiss: False
progress: 0
text: ''
title: "Drink Progress"
BoxLayout:
orientation: 'vertical'
Label:
text: root.text
size_hint: 1, 0.8
ProgressBar:
value: root.progress
FloatLayout:
Button:
text: 'Pour me a Drink!'
on_release: app.mix_drinks()
"""
)
class MyMainApp(App):
done = BooleanProperty(False)
def build(self):
return kv
@show_popup
def mix_drinks(self, popup): # Decorate function to show popup and run the code below in a thread
popup.text = 'Slicing limes...'
sleep(1)
popup.progress = 20
popup.text = 'Muddling sugar...' # Changing the popup attributes as the function runs!
sleep(2)
popup.progress = 50
popup.text = 'Pouring rum...'
sleep(2)
popup.progress = 80
popup.text = 'Adding ice...'
sleep(1)
popup.progress = 100
popup.text = 'Done!'
sleep(0.5)
self.done = True
if __name__ == '__main__':
MyMainApp().run()
在更新 CustomPopup
实例中的进度条和标签时,App.mix_drinks
在线程中运行 运行s。
添加取消按钮是另一种怪兽。如果你真的想这样做,你可以终止线程(参见 Is there any way to kill a Thread?),但我想不出一个简单的方法来做到这一点。