I am trying to load a webkit view on a different thread than main thread for gtk.
I see the example PyGTK, Threads and WebKit
I slightly modify for support PyGObject and GTK3:
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import WebKit
import threading
import time
# Use threads
Gdk.threads_init()
class App(object):
def __init__(self):
window = Gtk.Window()
webView = WebKit.WebView()
window.add(webView)
window.show_all()
#webView.load_uri('http://www.google.com') # Here it works on main thread
self.window = window
self.webView = webView
def run(self):
Gtk.main()
def show_html(self):
print 'show html'
time.sleep(1)
print 'after sleep'
# Update widget in main thread
GLib.idle_add(self.webView.load_uri, 'http://www.google.com') # Here it doesn't work
app = App()
thread = threading.Thread(target=app.show_html)
thread.start()
app.run()
Gtk.main()
The result it is a empty window and "after sleep" print is never executed. The idle_add call doesn't work. The only work part is the call commented on main thread.
I need GLib.threads_init() before gdk's.
Just like this:
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import WebKit
import threading
import time
# Use threads
GLib.threads_init()
class App(object):
def __init__(self):
window = Gtk.Window()
webView = WebKit.WebView()
window.add(webView)
window.show_all()
#webView.load_uri('http://www.google.com') # Here it works on main thread
self.window = window
self.webView = webView
def run(self):
Gtk.main()
def show_html(self):
print 'show html'
time.sleep(1)
print 'after sleep'
# Update widget in main thread
GLib.idle_add(self.webView.load_uri, 'http://www.google.com') # Here it doesn't work
app = App()
thread = threading.Thread(target=app.show_html)
thread.start()
app.run()
Gtk.main()
Related
I have a multi-window Gtk application, which is an installer.
During the installation process, which takes some time, I want to show a Window with a label to notify the user that the installation is in progress.
So I tried to bind the respective method to the show event.
However, that causes the appearance of the window to be delayed until the the method finishes, after which the next window is immediately shown.
The result is, that the previous window shows, then the screen goes blank for the duration of the actual installation and then the final window is shown.
I boiled the issue down to the fact, that the show event is obviously triggered, before the window is actually shown.
Here's a minimal snipped to clarify my issue.
The window shows after the call to sleep(), not before.
#! /usr/bin/env python3
from time import sleep
from gi import require_version
require_version('Gtk', '3.0')
from gi.repository import Gtk
class GUI(Gtk.ApplicationWindow):
def __init__(self):
"""Initializes the GUI."""
super().__init__(title='Gtk Window')
self.set_position(Gtk.WindowPosition.CENTER)
self.grid = Gtk.Grid()
self.add(self.grid)
self.label = Gtk.Label()
self.label.set_text('Doing stuff')
self.grid.attach(self.label, 0, 0, 1, 1)
self.connect('show', self.on_show)
def on_show(self, *args):
print('Doing stuff.')
sleep(3)
print('Done stuff.')
def main() -> None:
"""Starts the GUI."""
win = GUI()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
if __name__ == '__main__':
main()
How can I achieve, that the window shows before the method on_show() is called?
The desired program flow is
Show window
run installation
hide window (and show next one)
without any user interaction.
I fixed it by using multiprocessing :
from ast import Gt
from time import sleep
from multiprocessing import Process
from gi import require_version
require_version('Gtk', '3.0')
from gi.repository import Gtk
class GUI(Gtk.ApplicationWindow):
def __init__(self):
"""Initializes the GUI."""
super().__init__(title='Gtk Window')
self.set_position(Gtk.WindowPosition.CENTER)
self.grid = Gtk.Grid()
self.add(self.grid)
self.label = Gtk.Label()
self.label.set_text('Doing stuff')
self.grid.attach(self.label, 0, 0, 1, 1)
def on_show():
print('Doing stuff.')
sleep(3)
print('Done stuff.')
def main() -> None:
"""Starts the GUI."""
win = GUI()
win.connect('destroy', Gtk.main_quit)
win.show_all()
p1 = Process(target = on_show)
p1.start()
Gtk.main()
if __name__ == '__main__':
main()
Basically, what was happening is that Gtk.main() renders your app while show_all() just packs the widgets, so putting a sleep func before it delays the render.
another method is to use threading (which also provides you an easy way to implemant a progress bar) :
from ast import Gt
from time import sleep
import threading
from gi import require_version
require_version('Gtk', '3.0')
from gi.repository import Gtk
class GUI(Gtk.ApplicationWindow):
def __init__(self):
"""Initializes the GUI."""
super().__init__(title='Gtk Window')
self.set_position(Gtk.WindowPosition.CENTER)
self.grid = Gtk.Grid()
self.add(self.grid)
self.label = Gtk.Label()
self.label.set_text('Doing stuff')
self.grid.attach(self.label, 0, 0, 1, 1)
thread = threading.Thread(target=self.on_show)
thread.daemon = True
thread.start()
def on_show(self, *args):
print('Doing stuff.')
sleep(3)
print('Done stuff.')
def main() -> None:
"""Starts the GUI."""
win = GUI()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
if __name__ == '__main__':
main()
Further informations on threads
I want to use MPD's idle feature to wait for any changes and then display them in the GTK GUI using Python. The problem is that the GUI seems to block and become unresponsive when I use MPD's idle feature (when changing songs the GTK window becomes unresponsive). When I remove self.mpd.idle() it works, but then the function keeps getting run all the time which I find unnecessary.
What is the best way to solve this?
Not working
My initial approach:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib
from mpd import MPDClient
class GUI:
def __init__(self):
self.mpd = MPDClient()
self.mpd.timeout = 10
self.mpd.connect("localhost", 6600)
self.window = Gtk.Window()
self.window.connect("delete-event", Gtk.main_quit)
self.window.show_all()
GLib.idle_add(self.get_current_song)
Gtk.main()
def get_current_song(self):
self.mpd.idle()
print(self.mpd.currentsong())
return True
app = GUI()
Not working
My second approach using this. Still getting the same results.
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib
from mpd import MPDClient
import threading
class GUI:
def __init__(self):
self.mpd = MPDClient()
self.mpd.timeout = 1
self.mpd.connect("localhost", 6600)
self.window = Gtk.Window()
self.window.connect("delete-event", Gtk.main_quit)
self.window.show_all()
self.thread = threading.Thread(target=self.idle_loop)
self.thread.daemon = True
self.thread.start()
Gtk.main()
def get_songs(self):
print(self.mpd.currentsong())
self.mpd.idle()
return True
def idle_loop(self):
GLib.idle_add(self.get_songs)
app = GUI()
WORKING
Leaving out the GLib.idle_add() function seems to be a solution. But I don't know if this is the "proper" way. It feels wrong not knowing why GLib.idle_add() was messing it up and not using it since it's mentioned in the documentation.
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib
from mpd import MPDClient
import threading
class GUI:
def __init__(self):
self.mpd = MPDClient()
self.mpd.timeout = 1
self.mpd.connect("localhost", 6600)
self.window = Gtk.Window()
self.window.connect("delete-event", Gtk.main_quit)
self.window.show_all()
self.thread = threading.Thread(target=self.get_songs)
self.thread.daemon = True
self.thread.start()
Gtk.main()
def get_songs(self):
self.mpd.idle()
print(self.mpd.currentsong())
app = GUI()
Let us use the threading module here.
As described in this article : https://pygobject.readthedocs.io/en/latest/guide/threading.html, we can make the following code:
import gi
from threading import Thread
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib
from mpd import MPDClient
class GUI:
def __init__(self):
self.mpd = MPDClient()
self.mpd.timeout = 10
self.mpd.connect("localhost", 6600)
self.window = Gtk.Window()
self.window.connect("delete-event", Gtk.main_quit)
self.window.show_all()
thread = Thread(target=self.get_current_song, args=())
thread.start()
Gtk.main()
def get_current_song(self):
self.mpd.idle()
print(self.mpd.currentsong())
return True
app = GUI()
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.
Hello I'm need to show a sequnce of images whe the user make click in a button, I wrote the next code but only show me the las image... any idea what is wrong?
#!/usr/bin/python3
import os
import time
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
class GridWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="NEOTECH")
self.grid = Gtk.Grid()
self.add(self.grid)
self.btnStartTest=Gtk.Button("Iniciar Prueba")
self.btnStartTest.connect("clicked",self.StartTest)
self.image = Gtk.Image()
self.image.set_from_file("logo-NeoTech.png")
self.grid.add(self.btnStartTest)
self.grid.attach(self.image,0,1,1,1)
def StartTest(self,widget):
self.image.set_from_file("gato3.jpg")
time.sleep(2)
self.image.set_from_file("gato4.jpg")
print("fin")
win = GridWindow()
win.set_position(Gtk.WindowPosition.CENTER)
win.set_default_size(1000,480)
win.set_type_hint(Gdk.WindowTypeHint.MENU)
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Don't use time.sleep() in a GUI program; it blocks the GUI mainloop and makes it unresponsive.
Use GLib.timeout_add instead:
from gi.repository import Gtk, Gdk, GLib
class GridWindow(Gtk.Window):
def StartTest(self,widget):
self.image.set_from_file("gato3.jpg")
GLib.timeout_add(2000, self.show_last_image)
def show_last_image(self):
self.image.set_from_file("gato4.jpg")
print("fin")
I am creating an application. Previously, I were using Gtk.Main() to start my application, and created some hooks to stop the application from the command line using Ctrl+C. Now, I have migrated the application to a more "standard" Gtk.Application, but can't get it to stop using Ctrl+C.
This is a very simple Gtk.Application, that when is run from the command line, it can't be stopped using Ctrl+C:
from gi.repository import Gtk
import sys
# a Gtk ApplicationWindow
class MyWindow(Gtk.ApplicationWindow):
# constructor: the title is "Welcome to GNOME" and the window belongs
# to the application app
def __init__(self, app):
Gtk.Window.__init__(self, title="Welcome to GNOME", application=app)
class MyApplication(Gtk.Application):
# constructor of the Gtk Application
def __init__(self):
Gtk.Application.__init__(self)
# create and activate a MyWindow, with self (the MyApplication) as
# application the window belongs to.
# Note that the function in C activate() becomes do_activate() in Python
def do_activate(self):
win = MyWindow(self)
# show the window and all its content
# this line could go in the constructor of MyWindow as well
win.show_all()
# start up the application
# Note that the function in C startup() becomes do_startup() in Python
def do_startup(self):
Gtk.Application.do_startup(self)
# create and run the application, exit with the value returned by
# running the program
app = MyApplication()
exit_status = app.run(sys.argv)
sys.exit(exit_status)
import signal
from gi.repository import GLib
...
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, app.quit)
This worked with Gtk 3.0:
import signal
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
app = Application()
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, Gtk.main_quit)
You can use Ctrl+Z to stop the execution of script.