我有一个用 PyQt 创建的 GUI 应用程序,我希望能够通过一种内部 API 从 python 终端控制它。
想法:
- 使用主终端:不可能,因为它被 QApplication 阻止(通过 app.exec_())
- 在另一个线程中启动 GUI 以释放主线程:不可能,QApplications 必须在主线程中执行。
- ???
我不想要一个“应用内”终端。
你还有其他建议吗 ?
我有一个用 PyQt 创建的 GUI 应用程序,我希望能够通过一种内部 API 从 python 终端控制它。
想法:
我不想要一个“应用内”终端。
你还有其他建议吗 ?
使用主终端:不可能,因为它被 QApplication 阻止(通过 app.exec_())
app.exec()如果您在 Python 终端中并且它不会阻塞,则可以省略。正如这里所解释的,这很有效,因为......
PyQt5 安装了一个输入钩子(使用 PyOS_InputHook),它在交互式解释器等待用户输入时处理事件。这意味着您可以,例如,从 Python shell 提示符创建小部件,与它们交互,并且仍然能够输入其他 Python 命令。
例如,在 Python shell 中输入以下内容以同时拥有一个工作的 Qt 小部件和一个非阻塞 REPL。
$> python
Python 3.7.6 | packaged by conda-forge | (default, Jan 7 2020, 22:05:27)
[Clang 9.0.1 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from PyQt5.QtWidgets import QApplication, QWidget
>>> a = QApplication([])
>>> w = QWidget()
>>> w.show()
>>> w.raise_()
IPython 具有类似的功能。如果您以 开头ipython --gui=qt,或%gui qt在终端中键入,您将获得相同的效果...
$> ipython
Python 3.7.6 | packaged by conda-forge | (default, Jan 7 2020, 22:05:27)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.11.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: %gui qt
In [2]: from PyQt5 import QtWidgets
In [3]: win = QtWidgets.QPushButton("click me")
In [4]: win.show()
In [5]: win.raise_()
我建议使用 IPython,因为它更适合交互式工作,并且可以与 PySide 一起使用(也许普通的 Python 和 PySide 也可以;我没有检查)。
另请参阅我之前的答案
最后,即使这可行,我也不知道性能有多好。对于业余爱好项目来说,这是一个很好的解决方案,但如果您有很多用户,我会考虑实施“应用内”终端或某种形式的进程间通信。
根据您的要求,您想要实现类似于 Native Messaging Protocol ( Chrome , Mozilla )的东西,如果是这样,那么您应该使用QWinEventNotifier或QSocketNotifier依赖于操作系统来检测它是否是在控制台上编写的。
根据我之前的回答,我创建了以下示例,其中用户在控制台中写了一些短语,然后按Enter然后该短语显示在 QLabel 中(我只在 Linux 中测试了我的示例)。
import platform
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class NativeMessenger(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.m_qin = QtCore.QFile()
self.m_qin.open(
sys.stdin.fileno(), QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Unbuffered
)
if platform.system() == "Windows":
import win32api
if sys.platform == "win32":
import os
import msvcrt
if platform.python_implementation() == "PyPy":
os.fdopen(fh.fileno(), "wb", 0)
else:
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
self.m_notifier = QtCore.QWinEventNotifier(
win32api.GetStdHandle(win32api.STD_INPUT_HANDLE)
)
else:
self.m_notifier = QtCore.QSocketNotifier(
sys.stdin.fileno(), QtCore.QSocketNotifier.Read, self
)
self.m_notifier.activated.connect(self.readyRead)
@QtCore.pyqtSlot()
def readyRead(self):
line = self.m_qin.readLine().data().decode().strip()
self.messageChanged.emit(line)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QLabel(alignment=QtCore.Qt.AlignCenter)
w.resize(640, 480)
w.show()
messenger = NativeMessenger()
messenger.messageChanged.connect(w.setText)
sys.exit(app.exec_())
输出:
Stack Overflow
以上可以作为实现你的API的基础。
虽然另一种方法是有 2 个应用程序,其中 CLI 通过套接字和其他协议(例如 IPC(dbus 等)、ZeroMQ、MQTT 等)进行通信来控制 GUI。