DrawingArea Cannot Get XID - python

I have the following Python 2.7/PyGObject 3.0/PyGST 0.10 module:
from gi.repository import Gtk, Gdk, GdkPixbuf
import pango
import pygst
pygst.require('0.10')
import gst
import Trailcrest
import os, sys
import cairo
from math import pi
class Video:
def __init__(self):
def on_message(bus, message):
if message.type == gst.MESSAGE_EOS:
# End of Stream
player.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, 5000000000, gst.SEEK_TYPE_NONE, 6000000000)
elif message.type == gst.MESSAGE_ERROR:
player.set_state(gst.STATE_NULL)
(err, debug) = message.parse_error()
print "Error: %s" % err, debug
def on_sync_message(bus, message):
if message.structure is None:
return False
if message.structure.get_name() == "prepare-xwindow-id":
Gdk.threads_enter()
print "Run before"
Gdk.Display.get_default().sync()
print "Run after"
win_id = videowidget.window.xid
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(win_id)
Gtk.gdk.threads_leave()
def click_me(event, data=None):
player.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, 5000000000, gst.SEEK_TYPE_NONE, 6000000000)
win = Gtk.Window()
win.set_resizable(False)
win.set_decorated(False)
win.set_position(Gtk.WindowPosition.CENTER)
fixed = Gtk.Fixed()
win.add(fixed)
fixed.show()
videowidget = Gtk.DrawingArea()
fixed.put(videowidget, 0, 0)
videowidget.set_size_request(640, 480)
videowidget.show()
# Setup GStreamer
player = gst.element_factory_make("playbin", "MultimediaPlayer")
bus = player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
#used to get messages that GStreamer emits
bus.connect("message", on_message)
#used for connecting video to your application
bus.connect("sync-message::element", on_sync_message)
player.set_property("uri", "file://" + os.getcwd() + "/VID/BGA-HABT-001.ogv")
player.set_state(gst.STATE_PLAYING)
win.show()
def main():
Gdk.threads_enter()
Gtk.main()
return 0
if __name__ == "__main__":
Video()
main()
I am always getting this error, along with the video opening in a new window, instead of the existing window.
Traceback (most recent call last): File "video.py", line 32, in
on_sync_message
win_id = videowidget.window.xid AttributeError: 'DrawingArea' object has no attribute 'window'
How do I fix this, so the video displays in the window I created, instead of a new one?
By the by, this problem only started occuring after I switched to PyGObject 3.0 from PyGTK 2.24.

(Reprinted from GNOME PyGObject 3 Bug Report 663360. Answer credit goes to Timo Vanwynsberghe).
There are a couple of things to note:
- the drawingarea has to be realised before you can get its GdkWindow
- apparently, you can't get the window property directly
- you need to import GdkX11 for the xid method
With this in mind, here is a minimal working example:
from gi.repository import GdkX11, Gtk
class App:
def __init__(self):
win = Gtk.Window()
win.resize(400, 400)
win.connect('delete-event', Gtk.main_quit)
da = Gtk.DrawingArea()
win.add(da)
win.show_all()
print da.get_property('window').get_xid()
if __name__ == "__main__":
App()
Gtk.main()

Related

How to change a VideoOverlay's window handle after it has already been set?

Background
I'm looking for a way to change the window that my video is being rendered into. This is necessary because there are some situations where the window can be destroyed, for example when my application switches into fullscreen mode.
Code
When the canvas is realized, the video source and sink are connected. Then when the prepare-window-handle message is emitted, I store a reference to the VideoOverlay element that sent it. Clicking the "switch canvas" button calls set_window_handle(new_handle) on this element, but the video continues to render in the original canvas.
import sys
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gtk, Gst, GstVideo
Gst.init(None)
if sys.platform == 'win32':
import ctypes
PyCapsule_GetPointer = ctypes.pythonapi.PyCapsule_GetPointer
PyCapsule_GetPointer.restype = ctypes.c_void_p
PyCapsule_GetPointer.argtypes = [ctypes.py_object]
gdkdll = ctypes.CDLL('libgdk-3-0.dll')
gdkdll.gdk_win32_window_get_handle.argtypes = [ctypes.c_void_p]
def get_window_handle(widget):
window = widget.get_window()
if not window.ensure_native():
raise Exception('video playback requires a native window')
window_gpointer = PyCapsule_GetPointer(window.__gpointer__, None)
handle = gdkdll.gdk_win32_window_get_handle(window_gpointer)
return handle
else:
from gi.repository import GdkX11
def get_window_handle(widget):
return widget.get_window().get_xid()
class VideoPlayer:
def __init__(self, canvas):
self._canvas = canvas
self._setup_pipeline()
def _setup_pipeline(self):
# The element with the set_window_handle function will be stored here
self._video_overlay = None
self._pipeline = Gst.ElementFactory.make('pipeline', 'pipeline')
src = Gst.ElementFactory.make('videotestsrc', 'src')
video_convert = Gst.ElementFactory.make('videoconvert', 'videoconvert')
auto_video_sink = Gst.ElementFactory.make('autovideosink', 'autovideosink')
self._pipeline.add(src)
self._pipeline.add(video_convert)
self._pipeline.add(auto_video_sink)
# The source will be linked later, once the canvas has been realized
video_convert.link(auto_video_sink)
self._video_source_pad = src.get_static_pad('src')
self._video_sink_pad = video_convert.get_static_pad('sink')
self._setup_signal_handlers()
def _setup_signal_handlers(self):
self._canvas.connect('realize', self._on_canvas_realize)
bus = self._pipeline.get_bus()
bus.enable_sync_message_emission()
bus.connect('sync-message::element', self._on_sync_element_message)
def _on_sync_element_message(self, bus, message):
if message.get_structure().get_name() == 'prepare-window-handle':
self._video_overlay = message.src
self._video_overlay.set_window_handle(self._canvas_window_handle)
def _on_canvas_realize(self, canvas):
self._canvas_window_handle = get_window_handle(canvas)
self._video_source_pad.link(self._video_sink_pad)
def start(self):
self._pipeline.set_state(Gst.State.PLAYING)
window = Gtk.Window()
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
window.add(vbox)
canvas_box = Gtk.Box()
vbox.add(canvas_box)
canvas1 = Gtk.DrawingArea()
canvas1.set_size_request(400, 400)
canvas_box.add(canvas1)
canvas2 = Gtk.DrawingArea()
canvas2.set_size_request(400, 400)
canvas_box.add(canvas2)
player = VideoPlayer(canvas1)
canvas1.connect('realize', lambda *_: player.start())
def switch_canvas(btn):
handle = get_window_handle(canvas2)
print('Setting handle:', handle)
player._video_overlay.set_window_handle(handle)
btn = Gtk.Button(label='switch canvas')
btn.connect('clicked', switch_canvas)
vbox.add(btn)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()
Problem / Question
Calling set_window_handle() a 2nd time seems to have no effect - the video continues to render into the original window.
I've tried setting the pipeline into PAUSED, READY, and NULL state before calling set_window_handle(), but that didn't help.
I've also tried to replace the autovideosink with a new one as seen here, but that doesn't work either.
How can I change the window handle without disrupting the playback too much? Do I have to completely re-create the pipeline?
Looking at the source code, it appears that at least GL-based implementations of VideoOverlay element update the window id on expose event.
So you could try calling:
player._video_overlay.expose()
to reinitialize the GL scene after the window handle has been changed.
If that does not work, you can create a new VideoOverlay element and add it dynamically without stopping the graph.

Python Indicator stops updating after 3 times on Ubuntu

i am writing an Indicator in Python which should update every 5 minutes. Unfortunately the label is updated only 3 times, but then the label stays the same, although the thread still runs.
For demonstration purpose, I replaced the data with the current time.
#!/usr/bin/env python3
import signal
import gi
import threading
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject
import time
class Indicator():
def __init__(self):
self.app = 'turtle-mining-indicator'
self.menu = {}
self.indicator = AppIndicator3.Indicator.new(
self.app, ICON,
AppIndicator3.IndicatorCategory.OTHER)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
self.indicator.set_label("Starting ...", self.app)
self.scheduler()
def update(self):
print("update " + time.asctime())
self.indicator.set_label(time.asctime(), self.app)
self.menu["first"].set_label("First: " + time.asctime())
def scheduler(self):
self.update()
self.timer = threading.Timer(3, self.scheduler).start()
def create_menu(self):
menu = Gtk.Menu()
self.menu["first"] = Gtk.MenuItem("First")
menu.append(self.menu["first"])
menu.show_all()
return menu
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
I don't understand, why this happens. The method update() gets executed but the set_label method only works 3 times. What am I doing wrong?
Mixing threads and Gtk is just plain wrong. GLib.timeout_add_seconds() is made specifically for this reason. Here is the proper way to do timely updates:
#!/usr/bin/env python3
import signal
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GLib
import time
class Indicator():
def __init__(self):
self.app = 'turtle-mining-indicator'
self.menu = {}
self.indicator = AppIndicator3.Indicator.new(
self.app, "my indicator",
AppIndicator3.IndicatorCategory.OTHER)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.indicator.set_menu(self.create_menu())
self.indicator.set_label("Starting ...", self.app)
GLib.timeout_add_seconds (1, self.update)
def update(self):
print("update " + time.asctime())
self.indicator.set_label(time.asctime(), self.app)
self.menu["first"].set_label("First: " + time.asctime())
return True
def create_menu(self):
menu = Gtk.Menu()
self.menu["first"] = Gtk.MenuItem("First")
menu.append(self.menu["first"])
menu.show_all()
return menu
Indicator()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()

Running raw_input on a pygtk application

I need to make a graphical application that reads data from the console to update some widgets, this will have to do with 2 threads, one for the GUI and one for the console. The problem is that raw_input function does not work and also freezes the application. Here is the code.
import pygtk
pygtk.require('2.0')
import gtk
import gobject
from time import sleep
import sys
import threading
class Worker (threading.Thread):
def __init__(self, app):
threading.Thread.__init__(self)
self.app = app
def run(self):
text = raw_input("Enter some text: ") #It freezes the application
#text = "Hola" #It Works
self.app.writeMessage(text, False)
class Application:
def __init__(self, title, xPos, yPos):
gtk.threads_init()
self.win = gtk.Window()
screen = self.win.get_screen()
screenW = screen.get_width()
screenH = screen.get_height()
windowW = int(screenW * 0.5)
windowH = int(screenH * 0.25)
if type(xPos) is float:
xPos = int(screenW * xPos)
if type(yPos) is float:
yPos = int(screenH * yPos)
self.messageArea = gtk.TextView()
self.scroll = gtk.ScrolledWindow()
self.scroll.add(self.messageArea)
self.win.set_size_request(windowW, windowH)
self.win.set_title(title)
self.win.add(self.scroll)
self.win.show_all()
self.win.move(xPos, yPos)
self.win.connect("destroy", gtk.mainquit)
def doOperation(self, function, *args, **kw):
def idle_func():
try:
gtk.threads_enter()
function(*args, **kw)
return False
finally:
gtk.threads_leave()
gobject.idle_add(idle_func)
def sleeper():
time.sleep(.001)
return 1 # don't forget this otherwise the timeout will be removed
def mainloop(self):
#Trick for running threads and pygtk on win32 enviroment
if sys.platform == 'win32':
gtk.timeout_add(400, self.sleeper)
gtk.threads_enter()
gtk.mainloop()
gtk.threads_leave()
def writeMessage(self, message, isMainThread):
if isMainThread:
buf = self.messageArea.get_buffer()
end_iter = buf.get_end_iter()
buf.insert(end_iter, message)
else:
self.doOperation(self.writeMessage, message, True)
if __name__ == "__main__":
app = Application("Hello", 0, 0)
worker = Worker(app)
app.doOperation(worker.start)
app.mainloop()
Curiously the code only works if you run it in eclipse pydev, but it doesn't is the intention, I must run it from console. So this is the question, how to execute raw_input function and GUI on separate threads?
take a look at this.It explains about absence of event loop support in python and you can disable pygtk event loop after importing it.

GTK::Socket and Gtk::Plug unexpected behaviour under Gnome and FVWM2

The following code works fine if run within FVWM2. But if you change desktop to Gnome, then the embedded window is destroyed instead of being embedded.
Why is that? What am I missing?...
The code follows but basically all it does is fork. In the child, we create a VPython window an let it idle forever. In the parent, we create a GTK window, find out what the window ID of the child window is, and try to embed it vis a GTK::Socket.
Note that the VPython part maybe irrelevant to this.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import subprocess
import sys
import os
import re
import time
from visual import *
def find_window_id (title):
"""Gets the OpenGL window ID."""
pattern = re.compile('0x[0-9abcdef]{7}')
proc = subprocess.Popen(['xwininfo', '-name', title],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errors = proc.stderr.readlines()
if errors:
return None
for line in proc.stdout.readlines():
match = pattern.findall(line)
if len(match):
return long(match[0], 16)
return None
class Setting ():
"""VPython/OpenGL class."""
def __init__ (self, w=256, h=256, title='OpenGL via VPython'):
"""Initiator."""
self.width = w
self.height = h
self.title = title
self.scene = display.get_selected()
self.scene.title = self.title
self.scene.width = self.width
self.scene.height = self.height
self.sphere = sphere()
class GTKDisplay ():
def __init__ (self, winID):
"""Initiator: Draws the GTK GUI."""
import gtk
import pygtk
self.OpenGLWindowID = winID
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
window.connect("destroy", lambda w: gtk.main_quit())
socket.add_id(long(self.OpenGLWindowID))
gtk.main()
def main ():
"""Main entry point."""
name = 'sphere OpenGL window'
child_pid = os.fork()
if 0 == child_pid:
sut = Setting(title=name)
else:
winID = None
while not winID:
time.sleep(.1)
winID = find_window_id(name)
try:
gui = GTKDisplay(winID)
except KeyboardInterrupt, err:
print '\nAdieu monde cruel!'
if __name__ == "__main__":
main()
PS: Yes, this is a follow up from this question.

GTK window capture: VPython (OpenGL) application

Having read the documentation for VPython and GTK threading, it seems to me that it would be possible to embed VPython graphics within a gtk GUI. I know that it is possible with wx on Windows but I am on Linux and using PyGTK. Now, I have managed to get part of the way. I can embed a VPython window provided that it is spawned a separate process. What I would like is to embed it as a thread. The latter would make GUI events that control the OpenGL easier to implement -- via a thread instead of a socket and network calls.
Edit: Apparently nobody knows anything about this... Meh.
Here is the code I have. Uncomment the two commented out lines and comment a few obvious others and you can get to the process spawning code.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from visual import *
import threading
import Queue
import gtk
import pygtk
import re
import subprocess
class OPenGLThreadClass (threading.Thread):
"""Thread running the VPython code."""
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
self.name = 'OpenGLThread'
def run (self):
gtk.threads_enter()
self.scene = display.get_selected()
self.scene.title = 'OpenGL test'
s = sphere()
gtk.threads_leave()
#P = subprocess.Popen(['python', 'opengl.py'])
time.sleep(2)
self.queue.put(self.find_window_id())
self.queue.task_done()
def find_window_id (self):
"""Gets the OpenGL window ID."""
pattern = re.compile('0x[0-9abcdef]{7}')
P = subprocess.Popen(['xwininfo', '-name', self.scene.title],
#P = subprocess.Popen(['xwininfo', '-name', 'Visual WeldHead'],
stdout=subprocess.PIPE)
for line in P.stdout.readlines():
match = pattern.findall(line)
if len(match):
ret = long(match[0], 16)
print("OpenGL window id is %d (%s)" % (ret, hex(ret)))
return ret
class GTKWindowThreadClass (threading.Thread):
"""Thread running the GTK code."""
def __init__ (self, winID):
threading.Thread.__init__(self)
self.OpenGLWindowID = winID
self.name = 'GTKThread'
def run (self):
"""Draw the GTK GUI."""
gtk.threads_enter()
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
window.connect("destroy", lambda w: gtk.main_quit())
print("Got winID as %d (%s)" % (self.OpenGLWindowID, hex(self.OpenGLWindowID)))
socket.add_id(long(self.OpenGLWindowID))
gtk.main()
gtk.threads_leave()
def main ():
thread = {}
print("Embedding OpenGL/VPython into GTK GUI")
queue = Queue.Queue()
thread['OpenGL'] = OPenGLThreadClass(queue)
thread['OpenGL'].start()
winID = queue.get()
print("Got winID as %d (%s)" % (winID, hex(winID)))
gtk.gdk.threads_init()
thread['GTK'] = GTKWindowThreadClass(winID)
thread['GTK'].start()
if __name__ == "__main__":
main()
This is the code that works in case anyone cares.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import subprocess
import sys
import os
import re
import time
from visual import *
def find_window_id (title):
"""Gets the OpenGL window ID."""
pattern = re.compile('0x[0-9abcdef]{7}')
proc = subprocess.Popen(['xwininfo', '-name', title],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errors = proc.stderr.readlines()
if errors:
return None
for line in proc.stdout.readlines():
match = pattern.findall(line)
if len(match):
return long(match[0], 16)
return None
class Setting ():
"""VPython/OpenGL class."""
def __init__ (self, w=256, h=256, title='OpenGL via VPython'):
"""Initiator."""
self.width = w
self.height = h
self.title = title
self.scene = display.get_selected()
self.scene.title = self.title
self.scene.width = self.width
self.scene.height = self.height
self.sphere = sphere()
class GTKDisplay ():
def __init__ (self, winID):
"""Initiator: Draws the GTK GUI."""
import gtk
import pygtk
self.OpenGLWindowID = winID
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
window.connect("destroy", lambda w: gtk.main_quit())
socket.add_id(long(self.OpenGLWindowID))
gtk.main()
def main ():
"""Main entry point."""
name = 'sphere OpenGL window'
child_pid = os.fork()
if 0 == child_pid:
sut = Setting(title=name)
else:
winID = None
while not winID:
time.sleep(.1)
winID = find_window_id(name)
try:
gui = GTKDisplay(winID)
except KeyboardInterrupt, err:
print '\nAdieu monde cruel!'
if __name__ == "__main__":
main()
Note: This does not work under Gnome but works under fvwm2. Go figure...

Categories

Resources