Я написал следующий код, адаптируя это.
Запускается с помощью: python3 embed.py gnome-terminal
import time
import re
import subprocess
import sys, os, shutil
from PySide2.QtCore import (Qt, QProcess,)
from PySide2.QtGui import (QWindow,)
from PySide2.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMessageBox,)
class Window(QWidget):
def __init__(self, program, arguments):
super().__init__()
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
self.external = QProcess(self)
self.external.start(program, arguments)
time.sleep(1)
p = subprocess.run(['xprop', '-root'], stdout=subprocess.PIPE)
for line in p.stdout.decode().splitlines():
m = re.fullmatch(r'^_NET_ACTIVE_WINDOW.*[)].*window id # (0x[0-9a-f]+)', line)
if m:
self.embedWindow(int(m.group(1), 16))
# this is where the magic happens...
self.external.finished.connect(self.close_maybe)
break
else:
QMessageBox.warning(self, 'Error', 'Could not find WID for curreent Window')
def close_maybe(self):
pass
def closeEvent(self, event):
self.external.terminate()
self.external.waitForFinished(1000)
def embedWindow(self, wid):
window = QWindow.fromWinId(wid)
# window.setFlag(Qt.FramelessWindowHint, True)
widget = QWidget.createWindowContainer(
window, self, Qt.FramelessWindowHint)
self.layout().addWidget(widget)
if __name__ == '__main__':
if len(sys.argv) > 1:
if shutil.which(sys.argv[1]):
app = QApplication(sys.argv)
window = Window(sys.argv[1], sys.argv[2:])
window.setGeometry(100, 100, 800, 600)
window.show()
sys.exit(app.exec_())
else:
print('could not find program: %r' % sys.argv[1])
else:
print('usage: python %s <external-program-name> [args]' %
os.path.basename(__file__))
Полностью эквивалентная версия PyQt5 (просто косметические вариации и попытки передать больше флагов):
import os
import re
import shutil
import subprocess
import sys
import time
from PyQt5.QtCore import QProcess, Qt
from PyQt5.QtGui import QWindow
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox, QApplication
class Window(QWidget):
def __init__(self, program, arguments):
super().__init__()
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(layout)
self.external = QProcess(self)
self.external.start(program, arguments)
time.sleep(1)
p = subprocess.run(['xprop', '-root'], stdout=subprocess.PIPE)
for line in p.stdout.decode().splitlines():
m = re.fullmatch(r'^_NET_ACTIVE_WINDOW.*[)].*window id # (0x[0-9a-f]+)', line)
if m:
win = QWindow.fromWinId(int(m.group(1), 16))
win.setFlag(Qt.ForeignWindow, True)
win.setFlag(Qt.FramelessWindowHint, True)
win.setFlag(Qt.BypassGraphicsProxyWidget, True)
wid = QWidget.createWindowContainer(win, self, Qt.FramelessWindowHint)
self.layout().addWidget(wid)
# this is where the magic happens...
self.external.finished.connect(self.close_maybe)
break
else:
QMessageBox.warning(self, 'Error', 'Could not find WID for curreent Window')
def close_maybe(self):
pass
def closeEvent(self, event):
self.external.terminate()
self.external.waitForFinished(1000)
if __name__ == '__main__':
if len(sys.argv) > 1:
if shutil.which(sys.argv[1]):
app = QApplication(sys.argv)
window = Window(sys.argv[1], sys.argv[2:])
window.setGeometry(100, 100, 800, 600)
window.show()
sys.exit(app.exec_())
else:
print('could not find program: %r' % sys.argv[1])
else:
print('usage: python %s <external-program-name> [args]' %
os.path.basename(__file__))
ведет себя так же (неправильно). Я хочу иметь обе версии, чтобы охватить большее количество разработчиков, поскольку я подозреваю, что это довольно редкий случай, и не многие программисты обладают необходимым опытом.
У этого кода две проблемы:
- [неважно] Проверка закрытия (
self.external.finished.connect(self.close)
) не работает, потому чтоgnome-terminal
фактически связывается с базовымgnome-terminal-server
, запрашивает новое окно, а затем немедленно завершает работу. - [вопрос в тему] Встраивание не происходит. В итоге я получаю два разных окна (исходное
gnome-terminal
изменено и перемещено поверхMainWindow
, но остается отдельным).gnome-terminal
закрывается при завершении работы приложения, как и ожидалось.
Раскомментирование window.setFlag(Qt.FramelessWindowHint, True)
встраивания действительно происходит, но перерисовки нет, и я получаю довольно бесполезное окно с возможностью перемещения/изменения размера и статическим фоновым содержимым. Лежащий в основе gnome-terminal
функционален: ввод команд вслепую в замороженном окне *будет * выполнять их.
Настройка win.setFlag(Qt.ForeignWindow, True)
не имеет видимого эффекта.
Также обратите внимание, что определенная степень внедрения достигается, поскольку изменение размера окна приложения также изменит размер gnome-terminal
окна и переместит его в верхний левый угол экрана (0, 0
).
Это должно быть что-то с базовыми библиотеками Qt5, потому что я получаю точно такое же поведение и с PyQt5.
Что мне попробовать?
python3 embed.py gnome-terminal --wait
(а также с помощьюself.external.finished.connect(self.close)
). Я предполагаю, что ваши проблемы с внедрением вызваны тем, какой оконный менеджер вы используете. Для меня ни один из оконных флажков, кажется, не имеет никакого эффекта так или иначе. PS: я могу вводить команды и выполнять их только после нажатия на встроенное окно (т.е. путем переключения фокуса клавиатуры). - person ekhumoro   schedule 06.02.2021self.external.finished.connect(self.close)
может быть полезным, потому чтоesternal
действительно завершается довольно скоро (даже когда вы вводите gnome-terminal в командной строке, он возвращается немедленно, прежде чем фонgnome-terminal-server
открывает новое окно). - person ZioByte   schedule 06.02.2021external.finished
работает, потому что я проверял это. Вам нужно начатьgnome-terminal
с опцией--wait
. Я предлагаю вам попробовать это тоже, потому что это также может помочь с проблемой встраивания. Я использую openbox-3.6.1 с xorg-1.20.10, и у меня нет никаких проблем с внедрением gnome-terminal-3.38.2 (с опцией--wait
или без нее). - person ekhumoro   schedule 07.02.2021--wait
действительно работает и позволяет использоватьself.close
. К сожалению, это не меняет поведение встраивания. Сейчас я устанавливаю виртуальную машину VirtualBox (debian10/lxde) для дальнейшего тестирования. Спасибо. - person ZioByte   schedule 07.02.2021