Grab keyboard on XEmbed window - python

I'm trying to migrate open source application written in PyQt4 to Qt5 (PyQt5 or PySide2) and I have a problem with migration of QtGui.QX11EmbedContainer to Qt5. It looks that XEmbed container created with QtWidgets.QWidget.createWindowContainer cannot grab keyboard properly.
In Qt4 it was possible to grab keyboard in such way (for simplicity I'll attach xterm):
import sys
from PyQt4 import QtGui, QtCore
def clientEmbed():
container.grabKeyboard()
print("Keyboard grabbed")
def clientClosed():
container.releaseKeyboard()
print("Keyboard released")
app = QtGui.QApplication(sys.argv)
mainWidget = QtGui.QWidget()
container = QtGui.QX11EmbedContainer(mainWidget)
container.clientIsEmbedded.connect(clientEmbed)
container.clientClosed.connect(clientClosed)
vBoxLayout = QtGui.QVBoxLayout()
vBoxLayout.addWidget(container)
mainWidget.setLayout(vBoxLayout)
process = QtCore.QProcess(container)
winId = container.winId()
mainWidget.show()
process.start("xterm", "-into {} -xrm xterm*.allowSendEvents:true".format(winId).split())
sys.exit(app.exec_())
In this case any key event was sent to xterm, but in Qt5 analogous code doesn't work properly and xterm doesn't receives any key events:
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
app = QtWidgets.QApplication(sys.argv)
def processStarted():
container.grabKeyboard()
print("Process started")
mainWidget = QtWidgets.QWidget()
mainWidget.showMaximized()
window = QtGui.QWindow()
container = QtWidgets.QWidget.createWindowContainer(window, mainWidget)
vBoxLayout = QtWidgets.QVBoxLayout()
vBoxLayout.addWidget(container)
mainWidget.setLayout(vBoxLayout)
mainWidget.show()
winId = int(window.winId())
process = QtCore.QProcess(container)
process.started.connect(processStarted)
process.start("xterm", "-into {} -xrm xterm*.allowSendEvents:true".format(winId).split())
sys.exit(app.exec_())
At this time I'm considering migration from Qt4 to GTK3 which plays nice with grabbing keyboard on XEmbed, but it will be more time consuming. For example analogous working code in PyGTK:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
from subprocess import Popen
def plugged_added(plugged_socket):
Gdk.Seat.grab(seat, plugged_socket.get_window(), Gdk.SeatCapabilities.KEYBOARD, True)
print("xterm attached")
def plugged_removed(plugged_socket):
Gdk.Seat.ungrab(seat)
print("xterm detached")
seat = Gdk.Display.get_default().get_default_seat()
window = Gtk.Window(title="Xterm embed")
socket = Gtk.Socket()
window.add(socket)
sock_id = str(socket.get_id())
socket.connect("plug-added", plugged_added)
socket.connect("plug-removed", plugged_removed)
cmd = ["xterm", '-xrm', "xterm*.allowSendEvents: true", "-into", sock_id]
Popen(cmd)
socket.show()
window.show()
window.connect("destroy", Gtk.main_quit)
Gtk.main()
So does anybody have idea how to achieve grabbing keyboard in Qt5 like it is possible in Qt4 or PyGTK3? I'm also opened to solutions including xlib, xcb etc.

Related

Script is still running after closing pyqt5 window

import sys
import requests
from PyQt5.uic import loadUi
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QDialog, QApplication
class MainWindow(QDialog):
def __init__(self):
super(MainWindow, self).__init__()
loadUi("tabletutorial.ui",self)
self.request_function()
def request_function(self):
for i in range(0,100):
resp=requests.get("https://www.google.com")
print(resp.status_code)
# main
app = QApplication(sys.argv)
mainwindow = MainWindow()
widget = QtWidgets.QStackedWidget()
widget.addWidget(mainwindow)
widget.setFixedHeight(850)
widget.setFixedWidth(1120)
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")
This window is created by another main window.
Now the problem is when i quit the pyqt5 window the script is still running and i am getting the status code .I am running a big application with a bunch of requests.Anyone with relatable solution please ?
I TRIED:
self.close() not worked for me. #QtCore.pyqtSlot() also not worked.
I am new to here . Please ignore mistakes and kind answer are appriciated.

Trouble with executing "While" loop, using system tray on Python

I'm trying to create application which runs block of code every X seconds, which has system tray icon with only "Quit" option. But the problem is that when it get to the tray function, it doesn't read next lines of code, and as a result, "While" loop can't be launched.
Is there any other approach to do that?
import time
import os
import sys
from PySide2 import QtWidgets, QtGui
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
self.setToolTip(f'Wallpy')
menu = QtWidgets.QMenu(parent)
exit_ = menu.addAction("Exit")
exit_.triggered.connect(lambda: sys.exit())
menu.addSeparator()
self.setContextMenu(menu)
def tray():
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
tray_icon = SystemTrayIcon(QtGui.QIcon("tray.ico"), w)
tray_icon.show()
app.exec_()
def loop_function():
print("Nice") # anything executable
tray() # launch tray icon
while True:
loop_function() # executing every minute
time.sleep(60)
Its because, when you used the tray(), your main application started and the GUI main loop is initiated. It runs till your application exits, after which the while loop is executed.
However, if you want the loop to run simultaneously, you should integrate it with Qt's main loop. In Gtk we do this using GLib.add_main_thread and other methods, I don't know about Qt, but you can use this general solution using Threading.
from threading import Thread
import time
import os
import sys
from PySide2 import QtWidgets, QtGui
def loop_function():
print("Nice") # anything executable
def thread_loop():
while True:
loop_function() # executing every minute
time.sleep(60)
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon, parent=None):
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
self.setToolTip(f'Wallpy')
menu = QtWidgets.QMenu(parent)
exit_ = menu.addAction("Exit")
exit_.triggered.connect(lambda: sys.exit())
menu.addSeparator()
self.setContextMenu(menu)
def tray():
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QWidget()
tray_icon = SystemTrayIcon(QtGui.QIcon("tray.ico"), w)
tray_icon.show()
app.exec_()
my_loop = Thread(target=thread_loop, args=()) # Start the thread with no args
my_loop.start()
tray() # launch tray icon

Interrupting an IPython kernel embedded in a QT4 widget

I want to embed an Ipython terminal in a QT widget so I can access objects in my QT application. I understand that this is possible with examples such as this: https://github.com/gpoulin/python-test/blob/master/embedded_qtconsole.py
however, if the kernel is blocking (in an infinite while loop for example) the whole application is unresponsive and can only be killed with a keyboard interrupt from the terminal where the qt application was run. If I use the QtKernelManager instead of QtInProcessKernelManager I can successfully interrupt the kernel from the qt application, however, I cannot access objects within it as the kernel is embedded in a different process.
Is there a way to catch the keyboard interrupt when embedding the IPython terminal in the same process ? or should I use a different implementation of embedding the kernel ?
my adapted code is below
import os
os.environ['QT_API'] = 'pyqt'
import sip
sip.setapi("QString", 2)
sip.setapi("QVariant", 2)
import sys
from PyQt4 import QtGui, QtCore
from pkg_resources import require
require('ipython')
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
class IPythonWidget(QtGui.QWidget):
def __init__(self, **kwarg):
super(IPythonWidget, self).__init__()
self.initUI()
def startIpKernel(self):
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel_manager.kernel.gui = 'qt4'
self.kernel_client = kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
def initUI(self):
self.startIpKernel()
self.addWidgets()
def addWidgets(self):
self.button = QtGui.QPushButton()
self.button.setText("test button")
self.console = RichIPythonWidget(self)
self.console.kernel_manager =self.kernel_manager
self.console.kernel_client = self.kernel_client
self.console.kernel_manager.kernel.shell.push({"button": self.button})#this can be wrapped into another method
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.console)
vbox.addWidget(self.button)
self.setLayout(vbox)
def mousePressEvent(self, event):
self.mousepos = event.pos()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = IPythonWidget()
ex.show()
sys.exit(app.exec_())

Getting PySide Hello App to run under Canopy

A Canopy user here learning about PySide. When I run the demo code below, QApplication complains the event loop is already running.'
import sys
from PySide.QtCore import *
from PySide.QtGui import *
# Create a Qt application
#app = QApplication(sys.argv) #QApplication complains an instance already exists
app = QApplication.instance() #So we just ask for the instance.
#app.aboutToQuit.connect(app.deleteLater)
# Create a Label and show it
label = QLabel("Hello World")
label.show()
# Enter Qt application main loop
app.exec_()
sys.exit()
So how can I get this simple code to run?
Yes, Pylab is a mode of IPython which starts an event loop for the IPython front end so that you can interact at the IPython command line with your GUI.
Here's an simple example of code which will run with or without Pylab.
import sys
from PySide import QtGui
app = QtGui.QApplication.instance()
standalone = app is None
if standalone:
app = QtGui.QApplication(sys.argv)
wid = QtGui.QWidget()
wid.resize(250,150)
wid.setWindowTitle('Simple')
wid.show()
if standalone:
sys.exit(app.exec_())
else:
print "We're back with the Qt window still active"

PySide how to get QWebInspector same window

I just started dwelling into the realm of Qt (coming from PyGTK) and I'm using PySide. So I found this great example on another answer here on stack exchange.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *
app = QApplication(sys.argv)
web = QWebView()
web.settings().setAttribute(
QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
# or globally:
# QWebSettings.globalSettings().setAttribute(
# QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
web.load(QUrl("http://www.google.com"))
web.show()
inspect = QWebInspector()
inspect.setPage(web.page())
inspect.show()
sys.exit(app.exec_())
My question is as follows, how do I make the inspector show up in the same window instead of a new one? I understand I need to add the QWebInspector to another widget inside the main window (a vbox for example), what I want to know is how to connect that event to the signal the context menu "Inspect" triggers. In PyGTK I would need to use .connect() but I can't find the right SIGNAL for this specific action.
Thanks for your time guys/gals
It shouldn't be necessary to do anything special for the context menu to work. Just add an inspector widget to your layout, and hide() it to start with. The default context menu action can then show() the inspector as needed.
A slightly trickier issue is how to hide the inspector again once it's shown, as there doesn't seem to be a corresponding context menu item for that.
The demo script below simply creates a keyboard shortcut to hide/show the inspector:
from PySide import QtGui, QtCore, QtWebKit
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = QtWebKit.QWebView(self)
self.view.settings().setAttribute(
QtWebKit.QWebSettings.WebAttribute.DeveloperExtrasEnabled, True)
self.inspector = QtWebKit.QWebInspector(self)
self.inspector.setPage(self.view.page())
self.inspector.hide()
self.splitter = QtGui.QSplitter(self)
self.splitter.addWidget(self.view)
self.splitter.addWidget(self.inspector)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.splitter)
QtGui.QShortcut(QtGui.QKeySequence('F7'), self,
self.handleShowInspector)
def handleShowInspector(self):
self.inspector.setShown(self.inspector.isHidden())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.view.load(QtCore.QUrl('http://www.google.com'))
window.show()
sys.exit(app.exec_())

Categories