[英]What is the best way to generate 'Ctrl+c' event in python?
我正在開發一個 GUI,它可以使用 sounddevice 和 soundfile 庫錄制任意持續時間的音頻。 按“ctrl+c”組合鍵停止錄制過程。
我正在嘗試使用“開始”和“結束”按鈕實現 GUI。 “開始”按鈕應調用記錄 function,“結束”按鈕應模擬“ctrl+c”事件。 我不知道如何將此事件實現為 python 中的 function。非常感謝實現的想法。
如果您使用 windows 命令提示符python record.py
運行代碼,代碼將正常運行
記錄.py如下:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
import queue
from PyQt5 import QtCore, QtGui, QtWidgets
import soundfile as sf
import sounddevice as sd
import mythreading
class Ui_MainWindow(object):
def __init__(self):
self.threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(280, 190, 75, 23))
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(self.start_button_func)
self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_1.setGeometry(QtCore.QRect(380, 190, 75, 23))
self.pushButton_1.setObjectName("pushButton")
self.pushButton_1.clicked.connect(self.end_button_func)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Start"))
self.pushButton_1.setText(_translate("MainWindow", "End"))
def record(self):
self.q = queue.Queue()
self.s = sd.InputStream(samplerate=48000, channels=2, callback=self.callback)
try:
# Make sure the file is open before recording begins
with sf.SoundFile('check.wav', mode='x', samplerate=48000, channels=2, subtype="PCM_16") as file:
with self.s:
# 1 second silence before the recording begins
time.sleep(1)
print('START')
print('#' * 80)
print('press Ctrl+C to stop the recording')
while True:
file.write(self.q.get())
except OSError:
print('The file to be recorded already exists.')
sys.exit(1)
except KeyboardInterrupt:
print('The utterance is recorded.')
def callback(self, indata, frames, time, status):
"""
This function is called for each audio block from the record function.
"""
if status:
print(status, file=sys.stderr)
self.q.put(indata.copy())
def start_button_func(self):
self.worker = mythreading.Worker(self.record)
self.threadpool.start(self.worker)
def end_button_func(self):
print('how to stop?')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
mythreading.py如下:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Worker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
self.fn = fn
@pyqtSlot()
def run(self):
self.fn()
如果命令
exit()
使應用程序沒有響應我們可以模擬 ctrl 事件
signal.CTRL_C_EVENT
在窗戶里
和
signal.SIGINT
在 Linux 中
記住導入信號所以函數變成......
import signal
...
...
...
...
def end_button_func(self):
signal.SIGINT # if you are using ubuntu or mac
signal.CTRL_C_EVENT # if you are using windows
我有一台 Mac,所以我沒有嘗試 signal.CTRL_C_EVENT 所以無論如何都嘗試一下
希望它會起作用!
查看 sounddevice 的源代碼,看起來像KeyboardInterrupt
事件在他們的示例中調用了 argparser 的 exit 方法來記錄rec_unlimited.py ,
parser = argparse.ArgumentParser(description=__doc__)
...
try:
# Make sure the file is opened before recording anything:
with sf.SoundFile(args.filename, mode='x', samplerate=args.samplerate,
channels=args.channels, subtype=args.subtype) as file:
with sd.InputStream(samplerate=args.samplerate, device=args.device,
channels=args.channels, callback=callback):
print('#' * 80)
print('press Ctrl+C to stop the recording')
print('#' * 80)
while True:
file.write(q.get())
except KeyboardInterrupt:
print('\nRecording finished: ' + repr(args.filename))
parser.exit(0)
這個例子對你有用嗎? 我認為這里的退出無論如何都會簡單地調用系統退出。 如果這有效,那么從那里開始並確保您的 GUI 中的停止命令正在做同樣的事情(即引發KeyboardInterrupt
或退出)。
如果 sounddevice 正在創建線程(queue.get 建議這樣做),則使用線程也可能是一個問題,因此退出調用不會終止所有線程。
如果此退出在 Windows 中不起作用,則調用sd.stop()
可能是一個跨平台解決方案(盡管我懷疑無論如何都要離開with
塊)。
基於討論的更新:
由於示例工作正常,但end_button_func
凍結了 GUI,似乎記錄過程需要一個單獨的線程,因此您的 GUI 可以響應,直到傳遞信號以停止它。 我認為最好的方法是傳遞一個參數,當按下停止按鈕時,它會在線程中觸發KeyboardInterrupt
異常。 要與線程進行通信,您需要發送一個信號,如this answer或this中所示,並基於此,在線程中引發KeyboardInterrupt
。
這個例子( PyQt: How to send a stop signal into a thread where an object is running a Condition while loop? )似乎最接近,您可以在其中調整work
函數以引發異常,如下所示,
@pyqtSlot()
def work(self):
while self.running():
time.sleep(0.1)
print 'doing work...'
self.sgnFinished.emit()
raise KeyboardInterrupt
我這里沒有 PyQt5 或 windows,所以無法進一步測試。 請注意,您似乎需要將主類設為QObject
才能使用 pyqtSignal。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.