|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
import threading
|
|
|
|
|
import json
|
|
|
|
|
import socket
|
|
|
|
|
from PIL import Image, ImageTk
|
|
|
|
|
import os
|
|
|
|
|
import time
|
|
|
|
@ -8,6 +9,168 @@ from tkinter import messagebox
|
|
|
|
|
from tkinter import ttk
|
|
|
|
|
from tkinterdnd2 import DND_FILES, TkinterDnD
|
|
|
|
|
import shutil
|
|
|
|
|
import qrcode
|
|
|
|
|
# -*- coding:utf-8 -*-
|
|
|
|
|
#
|
|
|
|
|
# author: iflytek
|
|
|
|
|
#
|
|
|
|
|
# 本demo测试时运行的环境为:Windows + Python3.7
|
|
|
|
|
# 本demo测试成功运行时所安装的第三方库及其版本如下:
|
|
|
|
|
# cffi==1.12.3
|
|
|
|
|
# gevent==1.4.0
|
|
|
|
|
# greenlet==0.4.15
|
|
|
|
|
# pycparser==2.19
|
|
|
|
|
# six==1.12.0
|
|
|
|
|
# websocket==0.2.1
|
|
|
|
|
# websocket-client==0.56.0
|
|
|
|
|
# 合成小语种需要传输小语种文本、使用小语种发音人vcn、tte=unicode以及修改文本编码方式
|
|
|
|
|
# 错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
import websocket
|
|
|
|
|
import pyaudio
|
|
|
|
|
import datetime
|
|
|
|
|
import hashlib
|
|
|
|
|
import base64
|
|
|
|
|
import hmac
|
|
|
|
|
import json
|
|
|
|
|
from urllib.parse import urlencode
|
|
|
|
|
import time
|
|
|
|
|
import ssl
|
|
|
|
|
from wsgiref.handlers import format_date_time
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from time import mktime
|
|
|
|
|
import _thread as thread
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STATUS_FIRST_FRAME = 0 # 第一帧的标识
|
|
|
|
|
STATUS_CONTINUE_FRAME = 1 # 中间帧标识
|
|
|
|
|
STATUS_LAST_FRAME = 2 # 最后一帧的标识
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Ws_Param(object):
|
|
|
|
|
# 初始化
|
|
|
|
|
def __init__(self, APPID, APIKey, APISecret, Text):
|
|
|
|
|
self.APPID = APPID
|
|
|
|
|
self.APIKey = APIKey
|
|
|
|
|
self.APISecret = APISecret
|
|
|
|
|
self.Text = Text
|
|
|
|
|
|
|
|
|
|
# 公共参数(common)
|
|
|
|
|
self.CommonArgs = {"app_id": self.APPID}
|
|
|
|
|
# 业务参数(business),更多个性化参数可在官网查看
|
|
|
|
|
self.BusinessArgs = {"aue": "raw", "auf": "audio/L16;rate=12000", "vcn": "xiaoyan", "tte": "utf8"}
|
|
|
|
|
self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-8')), "UTF8")}
|
|
|
|
|
#使用小语种须使用以下方式,此处的unicode指的是 utf16小端的编码方式,即"UTF-16LE"”
|
|
|
|
|
#self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-16')), "UTF8")}
|
|
|
|
|
|
|
|
|
|
# 生成url
|
|
|
|
|
def create_url(self):
|
|
|
|
|
url = 'wss://tts-api.xfyun.cn/v2/tts'
|
|
|
|
|
# 生成RFC1123格式的时间戳
|
|
|
|
|
now = datetime.now()
|
|
|
|
|
date = format_date_time(mktime(now.timetuple()))
|
|
|
|
|
|
|
|
|
|
# 拼接字符串
|
|
|
|
|
signature_origin = "host: " + "ws-api.xfyun.cn" + "\n"
|
|
|
|
|
signature_origin += "date: " + date + "\n"
|
|
|
|
|
signature_origin += "GET " + "/v2/tts " + "HTTP/1.1"
|
|
|
|
|
# 进行hmac-sha256进行加密
|
|
|
|
|
signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
|
|
|
|
|
digestmod=hashlib.sha256).digest()
|
|
|
|
|
signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
|
|
|
|
|
|
|
|
|
|
authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
|
|
|
|
|
self.APIKey, "hmac-sha256", "host date request-line", signature_sha)
|
|
|
|
|
authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
|
|
|
|
|
# 将请求的鉴权参数组合为字典
|
|
|
|
|
v = {
|
|
|
|
|
"authorization": authorization,
|
|
|
|
|
"date": date,
|
|
|
|
|
"host": "ws-api.xfyun.cn"
|
|
|
|
|
}
|
|
|
|
|
# 拼接鉴权参数,生成url
|
|
|
|
|
url = url + '?' + urlencode(v)
|
|
|
|
|
# print("date: ",date)
|
|
|
|
|
# print("v: ",v)
|
|
|
|
|
# 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
|
|
|
|
|
# print('websocket url :', url)
|
|
|
|
|
return url
|
|
|
|
|
|
|
|
|
|
def on_message(ws, message):
|
|
|
|
|
try:
|
|
|
|
|
message =json.loads(message)
|
|
|
|
|
code = message["code"]
|
|
|
|
|
sid = message["sid"]
|
|
|
|
|
audio = message["data"]["audio"]
|
|
|
|
|
audio = base64.b64decode(audio)
|
|
|
|
|
status = message["data"]["status"]
|
|
|
|
|
print(message)
|
|
|
|
|
if status == 2:
|
|
|
|
|
print("ws is closed")
|
|
|
|
|
ws.close()
|
|
|
|
|
if code != 0:
|
|
|
|
|
errMsg = message["message"]
|
|
|
|
|
print("sid:%s call error:%s code is:%s" % (sid, errMsg, code))
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
|
|
with open('./demo.pcm', 'ab') as f:
|
|
|
|
|
f.write(audio)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print("receive msg,but parse exception:", e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 收到websocket错误的处理
|
|
|
|
|
def on_error(ws, error):
|
|
|
|
|
print("### error:", error)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 收到websocket关闭的处理
|
|
|
|
|
def on_close(ws):
|
|
|
|
|
print("### closed ###")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 收到websocket连接建立的处理
|
|
|
|
|
def on_open(ws):
|
|
|
|
|
def run(*args):
|
|
|
|
|
d = {"common": wsParam.CommonArgs,
|
|
|
|
|
"business": wsParam.BusinessArgs,
|
|
|
|
|
"data": wsParam.Data,
|
|
|
|
|
}
|
|
|
|
|
d = json.dumps(d)
|
|
|
|
|
print("------>开始发送文本数据")
|
|
|
|
|
ws.send(d)
|
|
|
|
|
if os.path.exists('./demo.pcm'):
|
|
|
|
|
os.remove('./demo.pcm')
|
|
|
|
|
|
|
|
|
|
thread.start_new_thread(run, ())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def saysay(text):
|
|
|
|
|
global wsParam
|
|
|
|
|
# 测试时候在此处正确填写相关信息即可运行
|
|
|
|
|
wsParam = Ws_Param(APPID='758c6465', APISecret='NTdjYWViNzcxZjI1YjFiY2QwOThjNTBl',
|
|
|
|
|
APIKey='8c4e835c82e54fbc84a4bf05f8a33972',
|
|
|
|
|
Text=text)
|
|
|
|
|
websocket.enableTrace(False)
|
|
|
|
|
wsUrl = wsParam.create_url()
|
|
|
|
|
ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
|
|
|
|
|
ws.on_open = on_open
|
|
|
|
|
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
|
|
|
|
|
p = pyaudio.PyAudio()
|
|
|
|
|
stream = p.open(format=p.get_format_from_width(2), channels=1, rate=12000, output=True)
|
|
|
|
|
|
|
|
|
|
# 将 pcm 数据直接写入 PyAudio 的数据流
|
|
|
|
|
with open("demo.pcm", "rb") as f:
|
|
|
|
|
stream.write(f.read())
|
|
|
|
|
|
|
|
|
|
stream.stop_stream()
|
|
|
|
|
stream.close()
|
|
|
|
|
p.terminate()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#image_references=[]
|
|
|
|
|
class logIn:#登录界面
|
|
|
|
|
def __init__(self):
|
|
|
|
@ -55,6 +218,7 @@ class admin:#管理员主界面
|
|
|
|
|
self.file12=tk.PhotoImage(file=".\\img\\cf.png")
|
|
|
|
|
self.file13=tk.PhotoImage(file=".\\img\\qrxg.png")
|
|
|
|
|
self.fileqr=tk.PhotoImage(file=".\\img\\qr.png")
|
|
|
|
|
self.filecode=tk.PhotoImage(file=".\img\\qrcode.png")
|
|
|
|
|
img=tk.Canvas(self.t,width=560,height=860)
|
|
|
|
|
img.create_image(280,430,image=file)
|
|
|
|
|
img.place(x=0,y=0)
|
|
|
|
@ -65,10 +229,42 @@ class admin:#管理员主界面
|
|
|
|
|
self.b2=tk.Button(self.t,relief="flat",image=self.file3,command=self.customer,bg="#EEDBCA",activebackground="#EEDBCA")
|
|
|
|
|
self.b3=tk.Button(self.t,relief="flat",image=self.file4,command=self.food,bg="#EEDBCA",activebackground="#EEDBCA")
|
|
|
|
|
self.b4=tk.Button(self.t,relief="flat",image=self.file5,command=self.psd,bg="#EEDBCA",activebackground="#EEDBCA")
|
|
|
|
|
self.b1.place(x=180,y=100)
|
|
|
|
|
self.b2.place(x=180,y=300)
|
|
|
|
|
self.b3.place(x=180,y=500)
|
|
|
|
|
self.b4.place(x=180,y=700)
|
|
|
|
|
self.b5=tk.Button(self.t,relief="flat",image=self.filecode,command=self.qrcode,bg="#EEDBCA",activebackground="#EEDBCA")
|
|
|
|
|
self.b1.place(x=180,y=30)
|
|
|
|
|
self.b2.place(x=180,y=200)
|
|
|
|
|
self.b3.place(x=180,y=370)
|
|
|
|
|
self.b4.place(x=180,y=540)
|
|
|
|
|
self.b5.place(x=180,y=710)
|
|
|
|
|
def makeqr(self):
|
|
|
|
|
os.system("start qrcode")
|
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
ipv4s=socket.gethostbyname_ex(socket.gethostname())[2]
|
|
|
|
|
ip=ipv4s[0]
|
|
|
|
|
for i in range(1,eval(self.eq1.get())+1):
|
|
|
|
|
qrcode.make("http://"+ip+":8080/{}".format(i)).save("qrcode/{}桌.png".format(i))
|
|
|
|
|
self.eq1.destroy()
|
|
|
|
|
self.b14.destroy()
|
|
|
|
|
self.b15.destroy()
|
|
|
|
|
self.admin_main()
|
|
|
|
|
def reqr(self):
|
|
|
|
|
self.eq1.destroy()
|
|
|
|
|
self.b14.destroy()
|
|
|
|
|
self.b15.destroy()
|
|
|
|
|
self.admin_main()
|
|
|
|
|
def qrcode(self):
|
|
|
|
|
self.b1.destroy()
|
|
|
|
|
self.b2.destroy()
|
|
|
|
|
self.b3.destroy()
|
|
|
|
|
self.b4.destroy()
|
|
|
|
|
self.b5.destroy()
|
|
|
|
|
self.eq1=tk.Entry(self.t,width=50)
|
|
|
|
|
self.eq1.insert("end","请输入最大桌号")
|
|
|
|
|
self.eq1.place(x=100,y=200)
|
|
|
|
|
self.b14=tk.Button(self.t,relief="flat",activebackground="#EEDBCA",image=self.fileqr,bg="#EEDBCA",command=self.makeqr)
|
|
|
|
|
self.b15=tk.Button(self.t,relief="flat",activebackground="#EEDBCA",image=self.file8,bg="#EEDBCA",command=self.reqr)
|
|
|
|
|
self.b14.place(x=180,y=480)
|
|
|
|
|
self.b15.place(x=180,y=580)
|
|
|
|
|
pass
|
|
|
|
|
def bif(self):
|
|
|
|
|
self.b18.destroy()
|
|
|
|
|
self.f2.destroy()
|
|
|
|
@ -95,29 +291,40 @@ class admin:#管理员主界面
|
|
|
|
|
self.e1.destroy()
|
|
|
|
|
self.e2.destroy()
|
|
|
|
|
self.admin_main()
|
|
|
|
|
def customerbill(self,last):
|
|
|
|
|
def customerbill(self,last,fl=1):
|
|
|
|
|
with open("customerconfig.json",encoding='utf-8') as f:
|
|
|
|
|
data=json.load(f)
|
|
|
|
|
if(last!=data):
|
|
|
|
|
last=data
|
|
|
|
|
saytext=""
|
|
|
|
|
self.tree=ttk.Treeview(self.f2,height=15,show="tree")
|
|
|
|
|
for i in data:
|
|
|
|
|
self.tree.insert("","end",iid=i,text="桌号:{}".format(i))
|
|
|
|
|
|
|
|
|
|
for jj in (data[i]):
|
|
|
|
|
for j in jj:
|
|
|
|
|
self.tree.insert(i,"end",text="餐品:{} 价格:{}".format(j,jj[j]))
|
|
|
|
|
self.tree.place(x=0,y=0,width=510)
|
|
|
|
|
time.sleep(1)
|
|
|
|
|
if self.flag: self.customerbill(last)
|
|
|
|
|
saytext+="{}桌的客人点餐:".format(i)
|
|
|
|
|
for jj in (data[i]):
|
|
|
|
|
for j in jj:
|
|
|
|
|
saytext+=j
|
|
|
|
|
print(fl)
|
|
|
|
|
if fl==1:saysay(saytext)
|
|
|
|
|
|
|
|
|
|
time.sleep(2)
|
|
|
|
|
if self.flag: self.customerbill(last,1)
|
|
|
|
|
def customer(self):
|
|
|
|
|
self.flag=True
|
|
|
|
|
self.b1.destroy()
|
|
|
|
|
self.b2.destroy()
|
|
|
|
|
self.b3.destroy()
|
|
|
|
|
self.b4.destroy()
|
|
|
|
|
self.b5.destroy()
|
|
|
|
|
|
|
|
|
|
self.f2=tk.Frame(height=312,width=520,relief='sunken',bd=5)
|
|
|
|
|
self.f2.place(x=20,y=50)
|
|
|
|
|
self.th=threading.Thread(target=lambda:self.customerbill(""))
|
|
|
|
|
self.th=threading.Thread(target=lambda:self.customerbill(last="",fl=0))
|
|
|
|
|
self.th.start()
|
|
|
|
|
self.b9=tk.Button(self.t,relief="flat",activebackground="#EEDBCA",image=self.file8,bg="#EEDBCA",command=self.re2)
|
|
|
|
|
self.b9.place(x=180,y=520)
|
|
|
|
@ -130,6 +337,8 @@ class admin:#管理员主界面
|
|
|
|
|
self.b2.destroy()
|
|
|
|
|
self.b3.destroy()
|
|
|
|
|
self.b4.destroy()
|
|
|
|
|
self.b5.destroy()
|
|
|
|
|
|
|
|
|
|
self.f2=tk.Frame(height=372,width=520,relief='sunken',bd=5)
|
|
|
|
|
self.tree2=ttk.Treeview(self.f2,height=18,show="tree")
|
|
|
|
|
self.f2.place(x=20,y=50)
|
|
|
|
@ -278,6 +487,8 @@ class admin:#管理员主界面
|
|
|
|
|
self.b2.destroy()
|
|
|
|
|
self.b3.destroy()
|
|
|
|
|
self.b4.destroy()
|
|
|
|
|
self.b5.destroy()
|
|
|
|
|
|
|
|
|
|
self.f3=tk.Frame(height=380,width=520,relief='sunken',bd=5)
|
|
|
|
|
#
|
|
|
|
|
name=[na['name'] for na in data]
|
|
|
|
@ -393,18 +604,32 @@ class admin:#管理员主界面
|
|
|
|
|
del data[id-1]
|
|
|
|
|
with open("foodconfig.json",'w',encoding='utf-8') as f:
|
|
|
|
|
json.dump(data,f,indent=2,sort_keys=True,ensure_ascii=False)
|
|
|
|
|
self.t.destroy()
|
|
|
|
|
new=admin()
|
|
|
|
|
new.food()
|
|
|
|
|
|
|
|
|
|
self.b10.destroy()
|
|
|
|
|
self.b11.destroy()
|
|
|
|
|
self.b12.destroy()
|
|
|
|
|
self.b18.destroy()
|
|
|
|
|
self.f3.destroy()
|
|
|
|
|
self.image_references=[]
|
|
|
|
|
self.food()
|
|
|
|
|
def qrc(self):
|
|
|
|
|
with open("foodconfig.json",encoding='utf-8') as f:
|
|
|
|
|
data=json.load(f)
|
|
|
|
|
data.append({"name":self.e1.get(),"price":eval(self.e2.get()),"img":'static/images/'+self.selected_image_path.split("/")[-1]})
|
|
|
|
|
with open("foodconfig.json",'w',encoding='utf-8') as f:
|
|
|
|
|
with open("foodconfig.json",'w+',encoding='utf-8') as f:
|
|
|
|
|
json.dump(data,f,indent=2,sort_keys=True,ensure_ascii=False)
|
|
|
|
|
shutil.copy(self.selected_image_path, 'static/images')
|
|
|
|
|
self.t.destroy()
|
|
|
|
|
admin()
|
|
|
|
|
try:shutil.copy(self.selected_image_path, 'static/images')
|
|
|
|
|
except:pass
|
|
|
|
|
self.t1.destroy()
|
|
|
|
|
self.t2.destroy()
|
|
|
|
|
self.e1.destroy()
|
|
|
|
|
self.e2.destroy()
|
|
|
|
|
self.f3.destroy()
|
|
|
|
|
self.b19.destroy()
|
|
|
|
|
self.qr.destroy()
|
|
|
|
|
self.imglabel.destroy()
|
|
|
|
|
self.food()
|
|
|
|
|
|
|
|
|
|
def on_drop(self,ev):
|
|
|
|
|
filepath=ev.data
|
|
|
|
|
self.selected_image_path=filepath
|
|
|
|
@ -421,6 +646,7 @@ class admin:#管理员主界面
|
|
|
|
|
self.f3.destroy()
|
|
|
|
|
self.b19.destroy()
|
|
|
|
|
self.qr.destroy()
|
|
|
|
|
self.imglabel.destroy()
|
|
|
|
|
self.food()
|
|
|
|
|
pass
|
|
|
|
|
def load_image(self,path, max_height=None):
|
|
|
|
@ -449,6 +675,7 @@ class admin:#管理员主界面
|
|
|
|
|
self.b2.destroy()
|
|
|
|
|
self.b3.destroy()
|
|
|
|
|
self.b4.destroy()
|
|
|
|
|
self.b5.destroy()
|
|
|
|
|
self.e1=tk.Entry(self.t,width=50)
|
|
|
|
|
self.e1.insert("end","请输入原密码")
|
|
|
|
|
self.e1.place(x=100,y=200)
|
|
|
|
|