问题描述
我有两个需要相互通信的Python脚本。第一个是在pyside2中制作的GUI。 GUI由用于控制蓝牙音频设备的简单控件组成(播放,暂停,下一个,上一个等)。这些命令与我发现的第二个python脚本一起运行。第二个脚本是一个循环,等待输入这些命令,并在这些命令执行后做出响应。我对编程还很陌生,我猜想这实际上是将前端与后端连接起来,但这是我以前从未做过的事情。
我已经编写了GUI的简化版本,仅显示我需要的控件。 “后端”也在下面,但最初可以在这里找到:https://scribles.net/controlling-bluetooth-audio-on-raspberry-pi/
我以前曾问过类似的问题,@ eyllanesc在这里给了可靠而有效的答案:Execute command to a Python script from separate Python script?但是,使用QProcess方法我无法弄清楚如何获得print
输出从后端到前端脚本。错误消息可正确打印。我尝试过在后端使用sys.stdout
,process.read
的变体和QByteArrays,但似乎什么也做不了。
我遇到的另一个问题是,只有在启动脚本之前连接了蓝牙设备,脚本才会起作用。如果我在运行时断开连接并尝试重新连接,它将不再接受命令。如果还有一种方法可以监视设备是否正在播放/暂停,以便可以根据设备状态更新“播放/暂停”按钮,那也很有用,但在此阶段并不重要。
有很多方法可以完成,但是我认为将两个脚本集成到一个最终对我来说会更好,但是我对任何可行的解决方案都持开放态度。如果有人有任何建议或可以帮助我入门,我将不胜感激!
前端:
import sys
from pyside2.QtWidgets import *
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.playbtn = QPushButton("Play")
self.nextbtn = QPushButton("Next")
self.prevbtn = QPushButton("Prev")
layout = QVBoxLayout()
layout.addWidget(self.playbtn)
layout.addWidget(self.nextbtn)
layout.addWidget(self.prevbtn)
self.setLayout(layout)
self.playbtn.released.connect(self.btnplay)
self.nextbtn.released.connect(self.btnnext)
self.prevbtn.released.connect(self.btnprev)
def btnplay(self): #play button turns into pause button upon being pressed
status = self.playbtn.text()
if status == "Play":
self.playbtn.setText("Pause")
print("Play pressed")
elif status == "Pause":
self.playbtn.setText("Play")
print("Pause pressed")
def btnnext(self):
print("Next pressed")
def btnprev(self):
print("Prev pressed")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
后端:
import dbus,dbus.mainloop.glib,sys
from gi.repository import GLib
def on_property_changed(interface,changed,invalidated):
if interface != 'org.bluez.MediaPlayer1':
return
for prop,value in changed.items():
if prop == 'Status':
print('Playback Status: {}'.format(value))
elif prop == 'Track':
print('Music Info:')
for key in ('Title','Artist','Album'):
print(' {}: {}'.format(key,value.get(key,'')))
def on_playback_control(fd,condition):
str = fd.readline()
if str.startswith('play'):
player_iface.Play()
elif str.startswith('pause'):
player_iface.Pause()
elif str.startswith('next'):
player_iface.Next()
elif str.startswith('prev'):
player_iface.PrevIoUs()
elif str.startswith('vol'):
vol = int(str.split()[1])
if vol not in range(0,128):
print('Possible Values: 0-127')
return True
transport_prop_iface.Set(
'org.bluez.MediaTransport1','Volume',dbus.UInt16(vol))
return True
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
obj = bus.get_object('org.bluez',"/")
mgr = dbus.Interface(obj,'org.freedesktop.DBus.ObjectManager')
player_iface = None
transport_prop_iface = None
for path,ifaces in mgr.GetManagedobjects().items():
if 'org.bluez.MediaPlayer1' in ifaces:
player_iface = dbus.Interface(
bus.get_object('org.bluez',path),'org.bluez.MediaPlayer1')
elif 'org.bluez.MediaTransport1' in ifaces:
transport_prop_iface = dbus.Interface(
bus.get_object('org.bluez','org.freedesktop.DBus.Properties')
if not player_iface:
sys.exit('Error: Media Player not found.')
if not transport_prop_iface:
sys.exit('Error: DBus.Properties iface not found.')
bus.add_signal_receiver(
on_property_changed,bus_name='org.bluez',signal_name='PropertiesChanged',dbus_interface='org.freedesktop.DBus.Properties')
GLib.io_add_watch(sys.stdin,GLib.IO_IN,on_playback_control)
GLib.MainLoop().run()
2020年10月31日更新:
我一直在研究上面链接的较早问题中建议的QProcess
类。通过在按钮按下功能上使用它并在执行命令后添加sys.exit
,它消除了始终连接设备的需要,但是我仍然找不到接收{{1}的方法}后端脚本的输出。这也感觉像是一种非常肮脏的工作方式。它还保留了播放/暂停状态未自动更新的问题。如果有人有任何建议,我将不胜感激!
print
解决方法
恕我直言,OP有一个XY问题,因为dbus eventloop可以与Qt共存,这给应用程序增加了不必要的复杂性,如以下示例所示:
import sys
import dbus
import dbus.mainloop.glib
from PyQt5 import QtCore,QtWidgets
class AudioManager(QtCore.QObject):
statusChanged = QtCore.pyqtSignal(str)
infoChanged = QtCore.pyqtSignal(dict)
def __init__(self,parent=None):
super().__init__(parent)
self._player_iface = None
self._transport_prop_iface = None
def initialize(self):
bus = dbus.SystemBus()
obj = bus.get_object("org.bluez","/")
mgr = dbus.Interface(obj,"org.freedesktop.DBus.ObjectManager")
player_iface = None
transport_prop_iface = None
for path,ifaces in mgr.GetManagedObjects().items():
if "org.bluez.MediaPlayer1" in ifaces:
player_iface = dbus.Interface(
bus.get_object("org.bluez",path),"org.bluez.MediaPlayer1"
)
elif "org.bluez.MediaTransport1" in ifaces:
transport_prop_iface = dbus.Interface(
bus.get_object("org.bluez","org.freedesktop.DBus.Properties"
)
if not player_iface:
raise Exception("Error: Media Player not found.")
if not transport_prop_iface:
raise Exception("Error: DBus.Properties iface not found.")
self._player_iface = player_iface
self._transport_prop_iface = transport_prop_iface
bus.add_signal_receiver(
self.handle_property_changed,bus_name="org.bluez",signal_name="PropertiesChanged",dbus_interface="org.freedesktop.DBus.Properties",)
def play(self):
self._player_iface.Play()
def pause(self):
self._player_iface.Pause()
def next(self):
self._player_iface.Next()
def previous(self):
self._player_iface.Previous()
def set_volume(self,Volume):
if Volume not in range(0,128):
raise ValueError("Possible Values: 0-127")
self._transport_prop_iface.Set(
"org.bluez.MediaTransport1","Volume",dbus.UInt16(vol)
)
def handle_property_changed(self,interface,changed,invalidated):
if interface != "org.bluez.MediaPlayer1":
return
for prop,value in changed.items():
if prop == "Status":
self.statusChanged.emit(value)
elif prop == "Track":
info = dict()
for key in ("Title","Artist","Album"):
info[key] = str(value.get(key,""))
self.infoChanged.emit(info)
class MainWindow(QtWidgets.QWidget):
def __init__(self,parent=None):
super().__init__(parent)
self._manager = AudioManager()
self._manager.infoChanged.connect(self.handle_info_changed)
self._manager.initialize()
self.playbtn = QtWidgets.QPushButton("Play")
self.nextbtn = QtWidgets.QPushButton("Next")
self.prevbtn = QtWidgets.QPushButton("Prev")
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.playbtn)
layout.addWidget(self.nextbtn)
layout.addWidget(self.prevbtn)
self.playbtn.released.connect(self._manager.play)
self.nextbtn.released.connect(self._manager.next)
self.prevbtn.released.connect(self._manager.previous)
def handle_info_changed(self,info):
print(info)
if __name__ == "__main__":
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
,
我不熟悉您在后端使用的内容,但是应该很简单:
# in your frontend
import NAME_OF_BACKEND_PY_FILE_HERE as backend
def btnplay(self): #play button turns into pause button upon being pressed
status = self.playbtn.text()
if status == "Play":
self.playbtn.setText("Pause")
print("Play Pressed")
backend.on_playback_control("play")
elif status == "Pause":
self.playbtn.setText("Play")
print("Pause pressed")
backend.on_playback_control("pause")
def btnnext(self):
print("Next pressed")
backend.on_playback_control("next")
def btnprev(self):
print("Prev pressed")
backend.on_playback_control("prev")
在后端,您还应该删除以下行:if __name__ == '__main__':
并取消缩进其下的所有代码。我不确定该如何正常调用该函数,或者不确定第二个变量“ condition”是做什么用的。但这就是我能想到的