Video Output In Tkinter From GStreamer? - python

does anyone know how i would go about using a tkinter window as an output from a videosink/pipeline from within python? i have found methods for lots of other GUI systems, but i dont want to have to use tkinter and something else together xxx
thanks in advance x

This works for me on Windows 32-bit. I get a seg fault on Linux or Windows 64-bit. Sorry, I don't know about Mac.
You have to use bus.connect("sync-message::element", on_sync_message) and pass a Tk widget ID (winfo_id), as you can see in the following code. The container can be any Tk widget, but a solid black frame seems to work best for me.
import sys, os
from Tkinter import *
import pygst
pygst.require("0.10")
import gst
def start():
player.set_property('video-sink', None)
player.set_property("uri", "file:///" + sys.argv[1])
player.set_state(gst.STATE_PLAYING)
def on_sync_message(bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
if message_name == "prepare-xwindow-id":
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(mwin_id)
window = Tk()
window.geometry("500x400")
movie_window = Frame(window,bg='#000000')
movie_window.pack(side=BOTTOM,anchor=S,expand=YES,fill=BOTH)
mwin_id = movie_window.winfo_id()
player = gst.element_factory_make("playbin2", "player")
fakesink = gst.element_factory_make('fakesink', 'novideo')
player.set_property('video-sink', fakesink)
bus = player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect("sync-message::element", on_sync_message)
start()
window.mainloop()

Related

Tray icon application in Python, icon is gone

I whipped up a little wallpaper-changing application for myself in Python. It makes an icon in the notification tray, which can be used to switch modes between "nice" and "naughty" depending on who's around :P and to force a wallpaper change. If left alone, it still changes to a random wallpaper once every 10 minutes, selecting randomly from a directory full of images that I continually add to. Everything was working great, until I upgraded from Ubuntu 14.04 "Trusty" to Ubuntu 15.10 "Wily." Now, the application still runs, and will change wallpapers once every 10 minutes like it should, but the icon is gone. It makes a space in the tray for the icon, but the icon no longer appears in it, nor does the empty space respond to any mouse clicks, left or right (left click used to force a wallpaper change, right click used to give me a menu of the two modes). No warning or error messages appear on the console when I run the application. I'm not too experienced with Python, and can't figure out what the hell is the problem. Following is the (very short) code for the applet. Sorry if there are any awful practices in the code, like I said I'm really not a python guy, it just seemed the easiest way to do what I wanted to do so I went with it. If anyone can help me figure out the problem I'd appreciate it!
PS "./change_wall" is just a bash script which does some other stuff besides just changing the wallpaper. I know the problem isn't there because the automatic wallpaper changes are still working, it's just the tray icon / control interface that's FUBAR.
#!/usr/bin/python
import os
import wx
import time
import thread
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self):
super(TaskBarIcon, self).__init__()
os.chdir("/home/caleb/walls")
self.SetIcon(wx.Icon('walls.xpm', wx.BITMAP_TYPE_XPM), "Wallpaper switcher")
self.set_nice(None)
self.Bind(wx.EVT_TASKBAR_LEFT_UP, self.set_new_wall_x)
def CreatePopupMenu(self):
menu = wx.Menu()
nice_item = menu.AppendRadioItem(-1, "Nice")
naughty_item = menu.AppendRadioItem(-1, "Naughty")
if self.type == 'nice':
nice_item.Check()
elif self.type == 'naughty':
naughty_item.Check()
menu.Bind(wx.EVT_MENU, self.set_nice, nice_item)
menu.Bind(wx.EVT_MENU, self.set_naughty, naughty_item)
return menu
def set_nice(self, event):
self.type = 'nice'
self.set_new_wall()
def set_naughty(self, event):
self.type = 'naughty'
self.set_new_wall()
def set_new_wall(self):
os.system("./change_wall " + self.type)
self.last_changed_time = time.time()
def set_new_wall_x(self, event):
self.set_new_wall()
def main():
app = wx.App(False)
the_icon = TaskBarIcon()
thread.start_new_thread(app.MainLoop, ())
while 1:
if (time.time() > the_icon.last_changed_time + 600):
the_icon.set_new_wall()
if __name__ == '__main__':
main()
Other than checking the things in my comment, see if this code functions, the icon used should exist on your machine.
import wx
TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = '/usr/share/icons/gnome/16x16/emotes/face-cool.png'
def create_menu_item(menu, label, func):
item = wx.MenuItem(menu, -1, label)
menu.Bind(wx.EVT_MENU, func, id=item.GetId())
menu.AppendItem(item)
return item
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self):
wx.TaskBarIcon.__init__(self)
self.set_icon(TRAY_ICON)
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)
self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.on_right_down)
def CreatePopupMenu(self):
menu = wx.Menu()
create_menu_item(menu, 'Say Hello', self.on_hello)
menu.AppendSeparator()
create_menu_item(menu, 'Exit', self.on_exit)
return menu
def set_icon(self, path):
icon = wx.IconFromBitmap(wx.Bitmap(path))
self.SetIcon(icon, TRAY_TOOLTIP)
def on_left_down(self, event):
print 'Tray icon was left-clicked.'
def on_right_down(self, event):
print 'Tray icon was right-clicked.'
event.Skip()
def on_hello(self, event):
print 'Hello, world!'
def on_exit(self, event):
wx.CallAfter(self.Destroy)
def main():
app = wx.App()
TaskBarIcon()
app.MainLoop()
if __name__ == '__main__':
main()
Well, rather than continue to muck around with wxPython and this Project Phoenix nonsense, I've switched to just using PyGTK and I have to say, overall I'm liking this a lot better. My code is now behaving exactly as I want it to, as follows:
#!/usr/bin/python
import gobject
import gtk
import os
import thread
import time
last_changed_time = 0
mode = "nice"
def set_mode (new_mode):
global mode
mode = new_mode
change_wall()
def make_menu(event_button, event_time, data=None):
menu = gtk.Menu()
nice_item = gtk.CheckMenuItem("Nice")
naughty_item = gtk.CheckMenuItem("Naughty")
kill_item = gtk.MenuItem("Quit")
if mode == "nice":
nice_item.set_active(True)
if mode == "naughty":
naughty_item.set_active(True)
menu.append(nice_item)
menu.append(naughty_item)
menu.append(kill_item)
nice_item.connect_object("activate", set_mode, ("nice"))
naughty_item.connect_object("activate", set_mode, ("naughty"))
kill_item.connect_object("activate", gtk.main_quit, ())
nice_item.show()
naughty_item.show()
kill_item.show()
menu.popup(None, None, None, event_button, event_time)
def change_wall():
global last_changed_time
os.system("./change_wall " + mode)
last_changed_time = time.time()
def on_right_click(data, event_button, event_time):
make_menu(event_button, event_time)
def on_left_click(event):
change_wall()
def auto_update():
while 1:
time.sleep(1)
if time.time() > last_changed_time + 600:
change_wall()
if __name__ == '__main__':
gobject.threads_init()
os.chdir("/home/caleb/walls")
icon = gtk.status_icon_new_from_file("walls.xpm")
icon.connect('popup-menu', on_right_click)
icon.connect('activate', on_left_click)
change_wall()
thread.start_new_thread(auto_update, ())
gtk.main()
I had to insert the time.sleep(1) call before every check to see whether it's time to auto-update in order to prevent the icon/menu itself from becoming rather laggy. I never ran into that problem when I was using wx; is there a more elegant way to solve this?
Thank you again for the help!
Because you are running wx.python 3 it looks like you will need to change a few of your calls:
IconFromBitmap becomes Icon
TaskBarIcon becomes adv.TaskBarIcon
There may be some other minor discrepancies but those appear to be the obvious ones.
Edit:
The other option is to load a previous version of wxpython.
For this, you can use wxversion like so, in your imports:
import wxversion
wxversion.select("2.8-gtk2-unicode")
import wx

hide window from MS windows taskbar

Using pyGtk I created a window without decoration. The Window is hidden from task bar and top of all windows. On linux it works fine, but on MS Windows window sometimes it hides under some other window and always has "python.exe" the taskbar in windows.
Image representing my problem:
How can I hide this "python.exe" window from taskbar?
My code:
class Infowindow(gtk.Window):
'''
Klasa okienka informacyjnego
'''
def __init__(self, json, index, destroy_cb, device):
gtk.Window.__init__(self)
self.size_x = 260+48
self.size_y = 85
self.separator_size = 10
self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_SPLASHSCREEN)
self.set_decorated(False)
self.set_property('skip-taskbar-hint', True)
self.set_opacity(1)
self.set_keep_above(True)
self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.connect("enter-notify-event", self.__on_hover)
self.connect("leave-notify-event", self.__on_leave)
self.connect("button_press_event", self.__on_click)
self.set_size_request(self.size_x, self.size_y)
color = gtk.gdk.color_parse('#f3f3f3')
self.modify_bg(gtk.STATE_NORMAL, color)
self.expanded = False
self.index = index
self.destroy_cb = destroy_cb
self.json = json['data']
self.system_info = False if 'system' not in self.json or not self.json['system'] else True
self.device = device
f = gtk.Frame()
self.move_window(index) #move window to specified place
self.box_area = gtk.VBox()
self.box_area.set_spacing(10)
f.add(self.box_area)
self.add(f)
self.show_all()
Again thanks David Heffernan. Works perfect!
For people who want a full solution in python.
Name your windowin a characteristic way for example: 'alamakota'
Use find_window('alamakota'),
With given handler use hide_from_taskbar(handler)
Last use set_topmost(handler)
Window is hidden from taskbar and it's alwoays on top.
I know it's not a beatyfull code, but works fine on windows XP and above.
import ctypes
import win32gui
import win32api
from win32con import SWP_NOMOVE
from win32con import SWP_NOSIZE
from win32con import SW_HIDE
from win32con import SW_SHOW
from win32con import HWND_TOPMOST
from win32con import GWL_EXSTYLE
from win32con import WS_EX_TOOLWINDOW
#staticmethod
def find_window(name):
try:
return win32gui.FindWindow(None, name)
except win32gui.error:
print("Error while finding the window")
return None
#staticmethod
def hide_from_taskbar(hw):
try:
win32gui.ShowWindow(hw, SW_HIDE)
win32gui.SetWindowLong(hw, GWL_EXSTYLE,win32gui.GetWindowLong(hw, GWL_EXSTYLE)| WS_EX_TOOLWINDOW);
win32gui.ShowWindow(hw, SW_SHOW);
except win32gui.error:
print("Error while hiding the window")
return None
#staticmethod
def set_topmost(hw):
try:
win32gui.SetWindowPos(hw, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE)
except win32gui.error:
print("Error while move window on top")
You have two options to remove a window from the taskbar:
Add the WS_EX_TOOLWINDOW extended window style. But this has other consequences and I cannot say whether or not they are serious.
Give the window an owner that is not visible. This allows you freedom to use whatever window styles and extended styles you wish, but does involve more work.
It's natural that your window will go beneath other windows. That's how windows works. If you want to make your window appear on top, show it with HWND_TOPMOST.
I've no idea how any of this is (or is not) implemented under PyGtk. I've just given you the Win32 answer!
Win32 solution provided in the other answer is not very easy and it does not play well with the GtkWindow::show method. A simple solution now in Gtk3 is:
win->set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_UTILITY); //This works
win->set_skip_taskbar_hint(); //This does not guarantee to work

python gtk gobject toggle visibility of windows

I wrote global shortcut example for hide and show my windows with key like 'F12', I used python-xlib and some script named 'pyxhook' everything works fine except when i want to hide() and show() window few times my proccess turning a zombie, but same code working with hiding and showing just button.
#!/usr/bin/python
# -*- coding: utf-8; -*-
from gi.repository import Gtk, GObject
from pyxhook import HookManager
GObject.threads_init()
class Win(Gtk.Window):
def __init__(self):
super(Win, self).__init__()
self.connect('destroy', Gtk.main_quit)
self.button = Gtk.Button()
self.add(self.button)
self.resize(200,150)
self.show_all()
def handle_global_keypress(self, event):
if event.Key == 'F12':
if self.get_visible():
self.hide()
else:
self.show()
### this part works fine with button
#if self.button.get_visible():
# self.button.hide()
#else:
# self.button.show()
def main():
app = Win()
hm = HookManager()
hm.HookKeyboard()
hm.KeyDown = app.handle_global_keypress
hm.start()
Gtk.main()
hm.cancel()
if __name__ == "__main__":
main()
edit: i solved my problem using Keybinder library instead of coding pure python keybinder.
http://kaizer.se/wiki/keybinder/
I'm unable to answer your specific question but I might suggest another option. Guake console implements this very same behavior but using dbus:
http://guake.org/
In the dbusiface.py file you can find:
import dbus
import dbus.service
import dbus.glib
import gtk
import guake.common
dbus.glib.threads_init()
DBUS_PATH = '/org/guake/RemoteControl'
DBUS_NAME = 'org.guake.RemoteControl'
class DbusManager(dbus.service.Object):
def __init__(self, guakeinstance):
self.guake = guakeinstance
self.bus = dbus.SessionBus()
bus_name = dbus.service.BusName(DBUS_NAME, bus=self.bus)
super(DbusManager, self).__init__(bus_name, DBUS_PATH)
#dbus.service.method(DBUS_NAME)
def show_hide(self):
self.guake.show_hide()
Among others methods. This is worth to explore. Please also note that Guake is developed using PyGtk and not PyGObject, but anyway you can get some ideas.

python and gstreamer, trying to play video (and later add textoverlay)

I'm trying to write a python application, and to get gstreamer to play a videofile I have recorded (and to have some subtitles on the video later on with textoverlay).
But looks like I still have some basic issues understanding how pads work.. I can't seem to get links up properly.
The basic example I am building on top is a simple application showing video from webcam. So I know the code works, and it's only my pipeline that is messing things up.
Also if I run execute following pipeline in terminal, it works:
gst-launch-0.10 filesrc location=GOPR0042.MP4 ! decodebin2 ! ffmpegcolorspace ! videoflip method=2 ! xvimagesink
Now, I am trying to recreate this pipeline to python app, as such:
#!/usr/bin/env python
import sys, os
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst
class GTK_Main:
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Webcam-Viewer")
window.set_default_size(500, 400)
window.connect("destroy", gtk.main_quit, "WM destroy")
vbox = gtk.VBox()
window.add(vbox)
self.movie_window = gtk.DrawingArea()
vbox.add(self.movie_window)
hbox = gtk.HBox()
vbox.pack_start(hbox, False)
hbox.set_border_width(10)
hbox.pack_start(gtk.Label())
self.button = gtk.Button("Start")
self.button.connect("clicked", self.start_stop)
hbox.pack_start(self.button, False)
self.button2 = gtk.Button("Quit")
self.button2.connect("clicked", self.exit)
hbox.pack_start(self.button2, False)
hbox.add(gtk.Label())
window.show_all()
# Set up the gstreamer pipeline
self.pipeline = gst.Pipeline("player")
self.filesource = gst.element_factory_make("filesrc","filesource")
self.filesource.set_property("location","""/home/jlumme/video/GOPR0042.MP4""")
self.pipeline.add(self.filesource)
self.decoder = gst.element_factory_make("decodebin2","decoder")
self.pipeline.add(self.decoder)
self.colorspace = gst.element_factory_make("ffmpegcolorspace","colorspace")
self.pipeline.add(self.colorspace)
self.videosink = gst.element_factory_make("xvimagesink","videosink")
self.pipeline.add(self.videosink)
self.filesource.link(self.decoder)
self.decoder.link(self.colorspace) #This fails
self.colorspace.link(self.videosink)
bus = self.pipeline.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect("message", self.on_message)
bus.connect("sync-message::element", self.on_sync_message)
def start_stop(self, w):
if self.button.get_label() == "Start":
self.button.set_label("Stop")
self.pipeline.set_state(gst.STATE_PLAYING)
else:
self.pipeline.set_state(gst.STATE_NULL)
self.pipeline.set_label("Start")
def exit(self, widget, data=None):
gtk.main_quit()
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.pipeline.set_state(gst.STATE_NULL)
self.button.set_label("Start")
elif t == gst.MESSAGE_ERROR:
err, debug = message.parse_error()
print "Error: %s" % err, debug
self.pipeline.set_state(gst.STATE_NULL)
self.button.set_label("Start")
def on_sync_message(self, bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
if message_name == "prepare-xwindow-id":
# Assign the viewport
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(self.movie_window.window.xid)
GTK_Main()
gtk.gdk.threads_init()
gtk.main()
Now I have seen people using a dynamic pad to link decoderbin to some audio stuff, but I don't really understand how it works...
So, I guess I can't connect decoderbin2 and ffmpegcolorspace directly ?
Could someone explain me why ?
Also, do you forsee problems in my next step, where I would like to add textoverlay element to the pipeline, to show subtitles ?
In the recent habit of answering my own questions, I will do that here as well :)
So, after a bit more reading and hacking, indeed I realize that I wasn't really getting the dynamic pads and, and how they need to be connected only when there is stuff coming in.
So basically I solved the above problem with 2 queues, for both audio and video. Those queues are connected then to decoders, and they need to be placed after the demuxer & connected dynamically.
Also decoder and sink seems to need a dynamic connecting of pads.
A question on this forum that explains this process very clearly is this one:
gstreamer code for playing avi file is hanging

Missing PyGST Plugin?

I'm running python 2.6 and PyGST (most recent available for Python 2.6). I've installed all the plugins - good, bad, and ugly.
I have the following code:
import sys, os
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst
class GTK_Main:
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Audio-Player")
window.set_default_size(300, -1)
window.connect("destroy", gtk.main_quit, "WM destroy")
vbox = gtk.VBox()
window.add(vbox)
self.entry = gtk.Entry()
vbox.pack_start(self.entry, False, True)
self.button = gtk.Button("Start")
self.button.connect("clicked", self.start_stop)
vbox.add(self.button)
window.show_all()
self.player = gst.element_factory_make("playbin2", "player")
fakesink = gst.element_factory_make("fakesink", "fakesink")
self.player.set_property("video_sink", fakesink)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.connect("message", self.on_message)
def start_stop(self, w):
if self.button.get_label() == "Start":
filepath = self.entry.get_text()
if os.path.isfile(filepath):
self.button.set_label("Stop")
self.player.set_property("uri", filepath)
self.player.set_state(gst.STATE_PLAYING)
else:
self.player.set_state(gst.STATE_NULL)
self.button.set_label("Start")
def on_message(self, bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.player.set_state(gst.STATE_NULL)
self.button.set_label("Start")
elif t == gst.MESSAGE_ERROR:
self.player.set_state(gst.STATE_NULL)
err, debug = message.parse_error()
print "Error: %s" % err, debug
self.button.set_label("Start")
GTK_Main()
gtk.gdk.threads_init()
gtk.main()
However, when I run this module and try to open a file, I get the following error.
Error: Your GStreamer installation is missing a plug-in.
..........\Source\gst-plugins-base\gst\playback\gsturidecodebin.c(991):
gen_source_element ():
/GstPlayBin2:player/GstURIDecodeBin:uridecodebin0
How do I correct this?
EDIT: The first answer works on Ubuntu, but it does not work on Windows. Bounty will be awarded to whoever solves the problem for Windows.
What you need is a full uri:
if you handle only files on your local filesystem and want to insert the file path only, change the line
self.player.set_property("uri", filepath)
to something like this:
self.player.set_property("uri", "file://"+filepath)
AND: GST has some routines to handle uri better the just prepending 'file://'.
A good tutorial is here
Some research has shown that this is apparently an error in a DLL. Tweaks may need to be made to the source, and the source compiled, instead of using WinBuilds.

Categories