如何将PyQt5 QInputDialog返回值传入AI助手主程序函数?
解决PyQt5中QInputDialog替代原生input()的输入传递问题
问题分析
- 直接在
techna2.py的Main函数中调用QInputDialog相关函数,导入时会重复启动GUI进程,引发报错。 - 通过GUI按钮触发QInputDialog后,无法将输入值传递给运行在子线程的
techna2.Main()函数。
可行方案
核心思路是利用Qt信号槽机制实现GUI线程与子线程的跨线程通信,结合线程同步工具实现阻塞式输入,替代原生input()函数。具体步骤如下:
步骤1:修改techna.py,添加输入通信逻辑
- 导入线程同步模块,在
GuiStart类中定义请求输入的信号、线程同步事件和输入结果存储变量。 - 实现弹窗输入的槽函数,以及供子线程调用的阻塞式输入获取方法。
- 修改子线程初始化逻辑,传入GUI实例引用,动态绑定输入函数到
techna2模块。
修改后的techna.py关键代码:
from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5.QtGui import QMovie, QTextBlock from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * from gui.technaui2 import Ui_technaui2 import sys import threading # 新增:导入线程同步模块 class MainThread(QThread): def __init__(self, gui_instance): super(MainThread, self).__init__() self.gui = gui_instance # 保存GUI实例引用 def run(self): self.TaskGui() def TaskGui(self): import techna2 # 动态绑定输入函数到techna2模块 techna2.get_user_input = self.gui.get_input while True: techna2.Main() class GuiStart(QMainWindow): # 定义请求输入的信号,参数为提示文本 request_input = pyqtSignal(str) def __init__(self): super().__init__() self.technaui2 = Ui_technaui2() self.technaui2.setupUi(self) self.technaui2.pushButton.clicked.connect(self.startFunc) self.technaui2.pushButton_2.clicked.connect(self.close) self.technaui2.btn1.clicked.connect(self.userInput2) sys.stdout = EmittingStream(textWritten=self.normalOutputWritten) # 线程安全的输入同步工具 self.input_ready = threading.Event() self.input_result = "" # 连接信号到弹窗槽函数 self.request_input.connect(self.show_input_dialog) def __del__(self): sys.stdout = sys.__stdout__ def normalOutputWritten(self, text): cursor = self.technaui2.textEdit.textCursor() cursor.movePosition(QtGui.QTextCursor.End) cursor.insertText(text) self.technaui2.textEdit.setTextCursor(cursor) self.technaui2.textEdit.ensureCursorVisible() def startFunc(self): self.technaui2.movies = QtGui.QMovie( "C:\Users\Hp\Desktop\SP\techna2\gui\images\gif\ai2.gif" ) self.technaui2.gif.setMovie(self.technaui2.movies) self.technaui2.movies.start() # 创建线程时传入当前GUI实例 global startFunction startFunction = MainThread(self) startFunction.start() def show_input_dialog(self, prompt): # 在GUI线程中显示输入弹窗 text, ok = QtWidgets.QInputDialog.getText(self, "用户输入", prompt) if ok: self.input_result = str(text) else: self.input_result = "" # 标记输入完成,唤醒等待的子线程 self.input_ready.set() def get_input(self, prompt="请输入:"): # 重置同步事件和输入结果 self.input_ready.clear() self.input_result = "" # 发送信号触发弹窗 self.request_input.emit(prompt) # 阻塞等待输入完成 self.input_ready.wait() return self.input_result def userInput2(self): # 保留原按钮功能,可将输入结果直接输出到文本框 text, ok = QtWidgets.QInputDialog.getText(self, "输入值", "请输入:") if ok: self.technaui2.textEdit.appendPlainText(f"用户输入:{text}") else: self.technaui2.textEdit.appendPlainText("输入无效") class EmittingStream(QtCore.QObject): textWritten = QtCore.pyqtSignal(str) def write(self, text): self.textWritten.emit(str(text)) guiApp = QApplication(sys.argv) guiTechna = GuiStart() guiTechna.show() exit(guiApp.exec_())
步骤2:修改techna2.py,替换原生input()
将Main函数中原生的input()调用替换为动态绑定的get_user_input()函数:
# ... 其他代码保持不变 ... def Main(): sentence = Listen() result = str(sentence) sentence = tokenize(sentence) X = bag_of_words(sentence, all_words) X = X.reshape(1, X.shape[0]) X = torch.from_numpy(X).to(device) output = model(X) _, predicted = torch.max(output, dim=1) tag = tags[predicted.item()] probs = torch.softmax(output, dim=1) prob = probs[0][predicted.item()] if prob.item() > 0.75: for intent in intents["intents"]: if tag == intent["tag"]: reply = random.choice(intent["responses"]) if "time" in reply: NonInputExecution(reply) elif "alarm" in reply: Say("For what time?") # 替换原生input()为自定义输入函数 time = get_user_input("请输入闹钟时间(例如:08:30)") if time: # 仅当输入有效时执行闹钟设置 Alarm(time) else: Say(reply)
方案说明
- 跨线程通信:通过
request_input信号将输入请求从子线程发送到GUI线程,确保弹窗在GUI线程中创建,避免线程安全问题。 - 阻塞式输入:使用
threading.Event实现子线程的阻塞等待,直到用户完成输入并触发事件唤醒,完美替代原生input()的阻塞行为。 - 避免重复启动GUI:通过动态绑定函数的方式,让
techna2模块无需导入GUI相关代码,彻底解决导入时重复启动GUI的问题。
内容的提问来源于stack exchange,提问作者Lily Johnson
相关产品推荐
相关产品推荐

