My setup is python 3.9 and PyQT5 on Windows 10
I have been using the QtGui.QWindow.fromWinId(<window_handle>)
function to successfully embed software like VNCviewer, WinSCP, etc. into a python gui. When I embed a console application (PuTTY SSH Session, Command Prompt) it does not accept keyboard or mouse inputs. The python window has focus but it appears the embedded window does not and doesn't respond to keyboard or mouse.
Code:
class MainWindow(QMainWindow):
"""
Launches the terminal
"""
def __init__(self, command: str, substring: str):
super(MainWindow, self).__init__()
widget = QWidget()
layout = QGridLayout(widget)
self.setCentralWidget(widget)
win = latchToWindow(command, substring)
hwnd = win._hWnd
self.window = QtGui.QWindow.fromWinId(hwnd)
self.windowcontainer = self.createWindowContainer(self.window, widget)
layout.addWidget(self.windowcontainer, 0, 0)
def latchToWindow(winLaunchCommand: str, gettitle: str):
"""
Used when there are additional dialogs and you need to grab a specific one
:param winLaunchCommand:
:param title:
:return:
"""
makeNewWindow(winLaunchCommand)
while True:
windows = pygetwindow.getAllWindows()
for win in windows:
tempTitle = win.title
if gettitle in tempTitle:
return win
time.sleep(0.1)
def makeNewWindow(winLaunchCommand: str):
"""
Makes a new window with that command, and returns that window
:param winLaunchCommand:
:return:
"""
windows = pygetwindow.getAllWindows()
os.popen(winLaunchCommand)
while True:
newWindow = pygetwindow.getAllWindows()
if len(newWindow) != len(windows):
for win in newWindow:
if win.title != "":
if win not in windows:
time.sleep(0.05)
return win
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("fusion")
# This doesn't work with keyboard and mouse
form = MainWindow("start cmd.exe", "cmd.exe")
# This does work with keyboard and mouse
// form = MainWindow("putty.exe", "PuTTY")
form.setWindowTitle('Embedded Python Terminal')
form.show()
app.exec()
[EDIT] I've learned that the problem is pyqt5 is in focus and the command prompt is out of focus. What needs to happen is when the embedded window is clicked that window is focused and the pyqt application needs to lose focus Any ideas on how to do that?
I've tried this solution but it does not work for me (scroll to last comment)
Preface: Use focus event handlers and not mouse move handlers to do this
You can use win32gui
to force the embedded window to be active even though it has been absorbed by qt.
win32gui.BringWindowToTop(hWnd)
win32gui.SetFocus(hWnd)
In this example I have included it to run every time it detects mouse movements inside the GUI. Ideally you would want it to execute every time you click inside the QWidget but I am relatively new to PyQT5 and have not learned how to do that properly.
Fixed code from example:
class MainWindow(QMainWindow):
"""
Launches the terminal
"""
def __init__(self, command: str, substring: str):
super(MainWindow, self).__init__()
win = latchToWindow(command, substring)
self.hwnd = win._hWnd
widget = widgetMouseTracking(self.hwnd)
widget.setMouseTracking(True)
layout = QGridLayout(widget)
self.setCentralWidget(widget)
self.window = QtGui.QWindow.fromWinId(self.hwnd)
self.windowcontainer = self.createWindowContainer(self.window, widget)
layout.addWidget(self.windowcontainer, 0, 0)
class widgetMouseTracking(QWidget):
def __init__(self, windowsID):
super().__init__()
self.id = windowsID
def mouseMoveEvent(self, a0: QtGui.QMouseEvent):
win32gui.BringWindowToTop(self.id)
win32gui.SetFocus(self.id)
def latchToWindow(winLaunchCommand: str, gettitle: str):
"""
Used when there are additional dialogs and you need to grab a specific one
:param winLaunchCommand:
:param title:
:return:
"""
makeNewWindow(winLaunchCommand)
while True:
windows = pygetwindow.getAllWindows()
for win in windows:
tempTitle = win.title
if gettitle in tempTitle:
return win
time.sleep(0.1)
def makeNewWindow(winLaunchCommand: str):
"""
Makes a new window with that command, and returns that window
:param winLaunchCommand:
:return:
"""
windows = pygetwindow.getAllWindows()
os.popen(winLaunchCommand)
while True:
newWindow = pygetwindow.getAllWindows()
if len(newWindow) != len(windows):
for win in newWindow:
if win.title != "":
if win not in windows:
time.sleep(0.05)
return win
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("fusion")
# This doesn't work with keyboard and mouse
form = MainWindow("start cmd.exe", "cmd.exe")
# This does work with keyboard and mouse
# form = MainWindow("putty.exe", "PuTTY")
form.setWindowTitle('Embedded Python Terminal')
form.show()
app.exec()