Как управлять / управлять / взаимодействовать PyQt GUI из командной строки в Python - PullRequest
0 голосов
/ 24 января 2020

У меня есть приложение GUI, созданное с помощью PyQt, и я хотел бы иметь возможность управлять им также из терминала python через своего рода внутренний API.

Идеи:

  • Использование основного терминала: невозможно, так как он заблокирован QApplication (app.exec _ ())
  • Запуск GUI в другом потоке, чтобы освободить основной: невозможно, приложения QApplications должны быть выполнены в главном.
  • ???

Я не хочу 'в приложении 'терминал.

У вас есть другие идеи?

Ответы [ 2 ]

2 голосов
/ 24 января 2020

В зависимости от того, что вы спрашиваете, вы хотите реализовать что-то похожее на Native Messaging Protocol (Chrome, Mozilla ) , если это так, тогда вы должны использовать QWinEventNotifier или QSocketNotifier в зависимости от ОС, чтобы определить, было ли это записано на консоли.

На основании моего предыдущего ответ Я создал следующий пример, где пользователь пишет в консоли какую-то фразу и нажимает Введите , затем эта фраза отображается в 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

enter image description here

Вышеприведенное можно взять за основу для реализации вашего API.


Хотя другой подход состоит в том, чтобы иметь 2 приложения, в которых CLI контролирует GUI, связываясь через сокеты и другие протоколы, такие как IP C (dbus, et c.), ZeroMQ, MQTT и др. c.

0 голосов
/ 24 января 2020

Использование основного терминала: невозможно, поскольку он заблокирован QApplication (app.exec _ ())

Вы можете опустить app.exec(), если находитесь в пределах Python терминал и он не будет блокировать. Как объяснено здесь , это работает, потому что ...

PyQt5 устанавливает ловушку ввода (используя PyOS_InputHook), которая обрабатывает события, когда интерактивный интерпретатор ожидает ввода данных пользователем. Это означает, что вы можете, например, создавать виджеты из командной строки Python, взаимодействовать с ними и по-прежнему иметь возможность вводить другие команды Python.

Например, введите следующее в оболочке Python иметь одновременно работающий виджет 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_()

I Python имеет аналогичную функциональность. Если вы начнете с 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_()

Я рекомендую использовать I Python, потому что он лучше подходит для интерактивной работы и он будет работать с PySide (возможно, обычный Python и PySide также будет работать; я не проверял).

См. Также мой предыдущий ответ здесь

Наконец, хотя это работает, я не знаю, насколько хороша производительность. Это хорошее решение для хобби-проекта, но если у вас много пользователей, я бы подумал о внедрении терминала в приложении или какой-либо форме межпроцессного взаимодействия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...