鱼C论坛

 找回密码
 立即注册
查看: 427|回复: 5

flask 写app遇到的问题

[复制链接]
发表于 2024-8-3 14:34:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
我用flask写了一个听力的app,只有第一个单词发声音,后面就没有声音了,怎么回事?

app.py
  1. from flask import Flask, render_template, request, jsonify
  2. import json
  3. import random
  4. import pyttsx3
  5. import threading
  6. import time
  7. import os

  8. app = Flask(__name__)


  9. current_dir = os.path.join(os.path.abspath(__file__))
  10. parent_dir = os.path.dirname(current_dir)
  11. filepath = os.path.join(parent_dir, 'words.json')

  12. # Load words from JSON file
  13. with open(filepath, 'r', encoding='utf-8') as f:
  14.     words_dict = json.load(f)

  15. # Initialize TTS engine
  16. engine = pyttsx3.init()
  17. engine.setProperty("rate", 100)
  18. engine.setProperty("volume", 1)
  19. voices = engine.getProperty("voices")

  20. # Global control variables
  21. is_paused = False
  22. stop_thread = False

  23. @app.route('/')
  24. def index():
  25.     return render_template('index.html')

  26. @app.route('/start', methods=['POST'])
  27. def start():
  28.     global is_paused, stop_thread
  29.     is_paused = False
  30.     stop_thread = False

  31.     language = request.form['language']
  32.     num_words = int(request.form['num_words'])
  33.     sleep_time = float(request.form['sleep_time'])
  34.     order = request.form['order']

  35.     words = list(words_dict.keys()) if language == 'english' else list(words_dict.values())
  36.     selected_words = random.sample(words, num_words) if order == 'random' else words[:num_words]

  37.     t = threading.Thread(target=display_and_pronounce, args=(selected_words, sleep_time, language))
  38.     t.start()
  39.     return jsonify({'status': 'started'})

  40. @app.route('/pause', methods=['POST'])
  41. def pause():
  42.     global is_paused
  43.     is_paused = not is_paused
  44.     return jsonify({'status': 'paused' if is_paused else 'resumed'})

  45. @app.route('/stop', methods=['POST'])
  46. def stop():
  47.     global stop_thread
  48.     stop_thread = True
  49.     return jsonify({'status': 'stopped'})

  50. def display_and_pronounce(words, sleep_time, language):
  51.     global is_paused, stop_thread
  52.     engine.setProperty('voice', voices[1].id if language == 'english' else voices[0].id)

  53.     for word in words:
  54.         if stop_thread:
  55.             break
  56.         while is_paused:
  57.             time.sleep(0.1)

  58.         gap = calculate_gap(word, sleep_time)
  59.         engine.say(word)
  60.         engine.runAndWait()
  61.         time.sleep(gap)
  62.         engine.say(word)
  63.         engine.runAndWait()
  64.         time.sleep(gap)

  65. def calculate_gap(word, sleep_time):
  66.     length = len(word.split(" "))
  67.     if length == 1:
  68.         return 2 * sleep_time
  69.     elif length == 2:
  70.         return 3 * sleep_time
  71.     elif length == 3:
  72.         return 5 * sleep_time
  73.     else:
  74.         return 9 * sleep_time

  75. if __name__ == "__main__":
  76.     app.run(debug=True)
复制代码


templates/index.html
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Language Learning App</title>
  6.     <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
  7.     <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  8. </head>
  9. <body>
  10.     <div class="container">
  11.         <h1>Language Learning App</h1>
  12.         <form id="control-form">
  13.             <label for="language">Select Language:</label>
  14.             <select id="language" name="language">
  15.                 <option value="english">English</option>
  16.                 <option value="chinese">Chinese</option>
  17.             </select>
  18.             <br>
  19.             <label for="num_words">Number of Words:</label>
  20.             <input type="number" id="num_words" name="num_words" value="10">
  21.             <br>
  22.             <label for="sleep_time">Sleep Time (seconds):</label>
  23.             <input type="number" step="0.1" id="sleep_time" name="sleep_time" value="1.0">
  24.             <br>
  25.             <label for="order">Order:</label>
  26.             <select id="order" name="order">
  27.                 <option value="sequential">Sequential</option>
  28.                 <option value="random">Random</option>
  29.             </select>
  30.             <br>
  31.             <button type="button" onclick="startLearning()">Start</button>
  32.             <button type="button" onclick="pauseLearning()">Pause/Resume</button>
  33.             <button type="button" onclick="stopLearning()">Stop</button>
  34.         </form>
  35.     </div>
  36.     <script>
  37.         function startLearning() {
  38.             $.post('/start', $('#control-form').serialize(), function(data) {
  39.                 console.log(data.status);
  40.             });
  41.         }
  42.         function pauseLearning() {
  43.             $.post('/pause', function(data) {
  44.                 console.log(data.status);
  45.             });
  46.         }
  47.         function stopLearning() {
  48.             $.post('/stop', function(data) {
  49.                 console.log(data.status);
  50.             });
  51.         }
  52.     </script>
  53. </body>
  54. </html>
复制代码


static/style.css
  1. body {
  2.     font-family: Arial, sans-serif;
  3.     background-color: #f4f4f4;
  4.     text-align: center;
  5.     padding: 50px;
  6. }

  7. .container {
  8.     background-color: #fff;
  9.     padding: 20px;
  10.     border-radius: 5px;
  11.     box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  12.     display: inline-block;
  13.     text-align: left;
  14. }

  15. h1 {
  16.     margin-bottom: 20px;
  17. }

  18. label {
  19.     display: block;
  20.     margin: 10px 0 5px;
  21. }

  22. input, select, button {
  23.     width: 100%;
  24.     padding: 10px;
  25.     margin: 5px 0;
  26.     border-radius: 5px;
  27.     border: 1px solid #ccc;
  28. }

  29. button {
  30.     background-color: #28a745;
  31.     color: white;
  32.     border: none;
  33.     cursor: pointer;
  34. }

  35. button:hover {
  36.     background-color: #218838;
  37. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-8-3 14:36:32 | 显示全部楼层
哦,看来你的 Flask 听力应用遇到了“一个字母都不想说”的尴尬局面。这种情况通常是由于多线程和 `pyttsx3` 的工作方式造成的。让我们逐步解决这个问题。

首先,`pyttsx3` 在调用 `runAndWait()` 时会阻塞当前线程,这意味着在此期间,它不会处理其他任何事情,包括你想说的下一个单词。因此,如果在同一线程中调用多个 `engine.say()` 和 `engine.runAndWait()`,就会导致问题。

下面是一些可能的解决方案:

### 解决方案

1. **避免重复调用 `runAndWait()`**:
   你可以通过将所有要说的单词放进一个列表,并只调用一次 `runAndWait()` 来避免问题。这样,Flask 线程就不会被阻塞。

   修改 `display_and_pronounce` 函数如下:

  
  1. def display_and_pronounce(words, sleep_time, language):
  2.        global is_paused, stop_thread
  3.        engine.setProperty('voice', voices.id if language == 'english' else voices.id)

  4.        for word in words:
  5.            if stop_thread:
  6.                break
  7.            while is_paused:
  8.                time.sleep(0.1)

  9.            gap = calculate_gap(word, sleep_time)
  10.            engine.say(word)
  11.            # 等待发音完成前不再调用
  12.            time.sleep(gap)  # 为了给发音留出时间
复制代码


2. **考虑使用队列**:
   在复杂的应用中,可以使用队列(如 `queue.Queue`)来管理待说的单词。这将使得处理更为灵活。

3. **检查声音设备**:
   确保你的音频设备不会因为多次切换而出现问题。有时候,操作系统对声音输出的处理也会影响。

4. **调试信息**:
   添加一些调试信息,比如在每次发音之前打印当前正在发音的单词,以确保代码逻辑没有问题。

### 其他小建议
- **改进用户体验**:前端的按钮可以在点击后禁用,防止用户疯狂地点击,让服务器喘口气。
- **异常处理**:加上异常处理机制,至少能在控制台看到一些错误提示,不让你的 APP 完全“安静”。

### 总结
以上是解决这个发声只停在第一个单词问题的一些建议。如果你像个歌手一样,想让每个单词都响亮且流畅,那你可能得调整下 `engine.runAndWait()` 的调用方式。而如果这些措施还不能解决问题,请把调试时的错误信息扔出来,我们一起开个会讨论!希望你能早日让你的小应用“发声”!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-8-3 15:15:07 | 显示全部楼层
改了之后根本不发声了。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-3 15:16:25 | 显示全部楼层
newbison 发表于 2024-08-03 15:15
改了之后根本不发声了。。。


哎呀,看来我们又打开了“调试黑洞”的大门!让我们再认真检查一下,并确保你的 Flask 应用声如洪钟,而不是小蚊子嗡嗡。

1. **移除重复调用 `runAndWait()`**:如果你只在循环内调用一次 `runAndWait()`,但要确保这个调用是在所有 `engine.say()` 之后。我们可以把逻辑稍作调整如下:

  1. def display_and_pronounce(words, sleep_time, language):
  2.     global is_paused, stop_thread
  3.     engine.setProperty('voice', voices.id if language == 'english' else voices.id)
  4.    
  5.     for word in words:
  6.         if stop_thread:
  7.             break
  8.             
  9.         while is_paused:
  10.             time.sleep(0.1)

  11.         # 说出单词
  12.         engine.say(word)
  13.         
  14.         # 等待发音完成后再处理下一个单词
  15.         engine.runAndWait()

  16.         # 等待 gap 时间
  17.         gap = calculate_gap(word, sleep_time)
  18.         time.sleep(gap)
复制代码


2. **确保 TTS 引擎正常工作**:在 Flask 线程中尝试手动调用一些简单的发音代码来确认引擎是否正常工作。例如,你可以在 Flask 的 `/start` 路由中增加以下调试语句:

  1. @app.route('/start', methods=['POST'])
  2. def start():
  3.     # ...其他代码...
  4.     engine.say("Testing text-to-speech engine")
  5.     engine.runAndWait()
  6.     return jsonify({'status': 'started'})
复制代码


如果你能听到“Testing text-to-speech engine”,那么证明 `pyttsx3` 一切正常;如果听不到,那可能是系统音频设置的问题。

3. **多线程使用问题**:这里的多线程需要小心。你也可以考虑将声音播放和 Flask 路由放在同一个线程中,以避免竞争条件造成的不稳定,比如:

  1. import queue

  2. # 创建队列
  3. sound_queue = queue.Queue()

  4. def play_sound():
  5.     while True:
  6.         word = sound_queue.get()  # 从队列取出要发音的单词
  7.         engine.say(word)
  8.         engine.runAndWait()
  9.         sound_queue.task_done()

  10. # 在主程序中启动音频播放线程
  11. t = threading.Thread(target=play_sound)
  12. t.daemon = True
  13. t.start()

  14. def display_and_pronounce(words, sleep_time, language):
  15.     global is_paused, stop_thread
  16.    
  17.     for word in words:
  18.         if stop_thread:
  19.             break

  20.         while is_paused:
  21.             time.sleep(0.1)

  22.         sound_queue.put(word)  # 将单词放入队列
  23.         time.sleep(calculate_gap(word, sleep_time))
复制代码


4. **修改后端路由方法**:确保你的 Flask 路由能正确处理请求并返回响应,让我们在 `/start` 的方法上确认它处理 POST 请求:

  1. @app.route('/start', methods=['POST'])
  2. def start():
  3.     # ...之前的代码...
  4.     return jsonify({'status': 'started'})
复制代码


### 最后的建议
如果这样还是不奏效,请务必在控制台查看是否有错误信息,或者在代码中添加更多的打印语句来检查执行流程。记住,调试就像是一场侦探游戏,任何蛛丝马迹都可能成为关键线索。而且,别忘了时不时看看你的音量设置哦,有时候最简单的问题才是幕后黑手!

希望这些修改和建议能帮助到你,如果还有问题,随时问我,我们一起捋顺这条“发声”的河流!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-8-3 15:40:06 | 显示全部楼层
能不能将app.py 整个文件给我一下?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-3 15:42:26 | 显示全部楼层
newbison 发表于 2024-08-03 15:40
能不能将app.py 整个文件给我一下?


当然可以!下面是一个经过调整的 `app.py` 文件,确保你的 Flask 应用可以顺利地发出声音,同时避免之前提到的问题:

  1. from flask import Flask, render_template, request, jsonify
  2. import json
  3. import random
  4. import pyttsx3
  5. import threading
  6. import time
  7. import os
  8. import queue

  9. app = Flask(__name__)

  10. # Initialize the sound queue
  11. sound_queue = queue.Queue()

  12. # Setup TTS engine
  13. engine = pyttsx3.init()
  14. engine.setProperty("rate", 100)
  15. engine.setProperty("volume", 1)
  16. voices = engine.getProperty("voices")

  17. # Load words from JSON file
  18. current_dir = os.path.join(os.path.abspath(__file__))
  19. parent_dir = os.path.dirname(current_dir)
  20. filepath = os.path.join(parent_dir, 'words.json')

  21. with open(filepath, 'r', encoding='utf-8') as f:
  22.     words_dict = json.load(f)

  23. # Global control variables
  24. is_paused = False
  25. stop_thread = False

  26. @app.route('/')
  27. def index():
  28.     return render_template('index.html')

  29. @app.route('/start', methods=['POST'])
  30. def start():
  31.     global is_paused, stop_thread
  32.     is_paused = False
  33.     stop_thread = False

  34.     language = request.form['language']
  35.     num_words = int(request.form['num_words'])
  36.     sleep_time = float(request.form['sleep_time'])
  37.     order = request.form['order']

  38.     words = list(words_dict.keys()) if language == 'english' else list(words_dict.values())
  39.     selected_words = random.sample(words, num_words) if order == 'random' else words[:num_words]

  40.     for word in selected_words:
  41.         sound_queue.put(word)  # Put each word in the queue

  42.     return jsonify({'status': 'started'})

  43. @app.route('/pause', methods=['POST'])
  44. def pause():
  45.     global is_paused
  46.     is_paused = not is_paused
  47.     return jsonify({'status': 'paused' if is_paused else 'resumed'})

  48. @app.route('/stop', methods=['POST'])
  49. def stop():
  50.     global stop_thread
  51.     stop_thread = True
  52.     return jsonify({'status': 'stopped'})

  53. def play_sound():
  54.     while True:
  55.         word = sound_queue.get()  # Get a word from the queue
  56.         if stop_thread:
  57.             break
  58.         engine.say(word)
  59.         engine.runAndWait()  # Wait until the speech is finished
  60.         sound_queue.task_done()  # Indicate that the word has been processed

  61. # Start the sound playing thread
  62. threading.Thread(target=play_sound, daemon=True).start()

  63. if __name__ == "__main__":
  64.     app.run(debug=True)
复制代码


### 主要变动说明:
1. **使用 `queue.Queue()`**:通过队列管理要发音的单词,避免卡住线程。
2. **简化音频播放逻辑**:将播放逻辑放到一个独立的线程中,确保每个单词都能按顺序发音。
3. **在路由中获取参数**:确保从请求中正确获取参数。

### 其他注意事项:
- 请确认 `words.json` 文件结构正确,以便正确加载单词。
- 确保 `pyttsx3` 以及其他依赖项已正确安装。
- 根据需求,记得在前端 HTML 中修改对应的字段名称和格式。

希望这个完整的 `app.py` 文件能帮你顺利运行。若还有其他问题,请随时告诉我!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-21 14:56

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表