How to continuously monitor rhythmbox for track change using python - python

I want to monitor the change of track in Rhythmbox using python. I want to continuously check for change of track and execute a set of functions if the track is changed. I have written a piece of code which gets hold of the Rhythmbox interfaces from the dbus and gets the current track details. But this program has to be run manually to check for any change.
I am new to this and I would like to know how we can create a background process which continuously runs and checks Rhythmbox.
I dont want to make a Rhythmbox plugin(which rather would make my work simple) as I will be extending the application to listen to multiple music players.
Please suggest me what exactly I would have to do to achieve the functionality.

The Rhythmbox player object (/org/gnome/Rhythmbox/Player) sends a playingUriChanged signal whenever the current song changes. Connect a function to the signal to have it run whenever the signal is received. Here's an example that prints the title of the song whenever a new song starts, using the GLib main loop to process DBus messages:
#! /usr/bin/env python
import dbus
import dbus.mainloop.glib
import glib
# This gets called whenever Rhythmbox sends the playingUriChanged signal
def playing_song_changed (uri):
global shell
if uri != "":
song = shell.getSongProperties (uri)
print "Now playing: {0}".format (song["title"])
else:
print "Not playing anything"
dbus.mainloop.glib.DBusGMainLoop (set_as_default = True)
bus = dbus.SessionBus ()
proxy = bus.get_object ("org.gnome.Rhythmbox", "/org/gnome/Rhythmbox/Player")
player = dbus.Interface (proxy, "org.gnome.Rhythmbox.Player")
player.connect_to_signal ("playingUriChanged", playing_song_changed)
proxy = bus.get_object ("org.gnome.Rhythmbox", "/org/gnome/Rhythmbox/Shell")
shell = dbus.Interface (proxy, "org.gnome.Rhythmbox.Shell")
# Run the GLib event loop to process DBus signals as they arrive
mainloop = glib.MainLoop ()
mainloop.run ()

Take a look at the Conky script here:
https://launchpad.net/~conkyhardcore/+archive/ppa/+files/conkyrhythmbox_2.12.tar.gz
That uses dbus to talk to rhythmbox, like so:
bus = dbus.SessionBus()
remote_object_shell = bus.get_object('org.gnome.Rhythmbox', '/org/gnome/Rhythmbox/Shell')
iface_shell = dbus.Interface(remote_object_shell, 'org.gnome.Rhythmbox.Shell')
remote_object_player = bus.get_object('org.gnome.Rhythmbox', '/org/gnome/Rhythmbox/Player')
iface_player = dbus.Interface(remote_object_player, 'org.gnome.Rhythmbox.Player')
You can call a number of functions on iface_player to get the required information. It looks like you'll have to poll from this example though. If you want to receive a message from dbus on track change you'll have to do that in a different way. This discusses from avenues to explore:
http://ubuntuforums.org/showthread.php?t=156706

I am using Ubuntu 14.04.1 and the above script is deprecated for Rhythmbox 3. I am using this script to write the current song to ~/.now_playing for BUTT to read, but you can update it for your needs. Rhythmbox uses MPRIS now and you can get info here:
http://specifications.freedesktop.org/mpris-spec/latest/index.html
#!/usr/bin/python
import dbus
import dbus.mainloop.glib
import glib
# This gets called whenever Rhythmbox sends the playingUriChanged signal
def playing_song_changed (Player,two,three):
global iface
global track
global home
track2 = iface.Get(Player,"Metadata").get(dbus.String(u'xesam:artist'))[0] + " - "+ iface.Get(Player,"Metadata").get(dbus.String(u'xesam:title'))
if track != track2:
track = iface.Get(Player,"Metadata").get(dbus.String(u'xesam:artist'))[0] + " - "+ iface.Get(Player,"Metadata").get(dbus.String(u'xesam:title'))
f = open( home + '/.now_playing', 'w' )
f.write( track + '\n' )
f.close()
dbus.mainloop.glib.DBusGMainLoop (set_as_default = True)
bus = dbus.SessionBus ()
from os.path import expanduser
home = expanduser("~")
player = bus.get_object ("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2")
iface = dbus.Interface (player, "org.freedesktop.DBus.Properties")
track = iface.Get("org.mpris.MediaPlayer2.Player","Metadata").get(dbus.String(u'xesam:artist'))[0] + " - "+ iface.Get("org.mpris.MediaPlayer2.Player","Metadata").get(dbus.Strin$
f = open( home + "/.now_playing", 'w' )
f.write( track + '\n' )
f.close()
iface.connect_to_signal ("PropertiesChanged", playing_song_changed)
# Run the GLib event loop to process DBus signals as they arrive
mainloop = glib.MainLoop ()
mainloop.run ()

Something like:
from time import sleep
execute = True
while execute:
your_function_call()
sleep(30) # in seconds; prevent busy polling
Should work just fine. If that was hooked up to something that listened to signals (import signal) so that you could set execute to False when someone ctrl-c's the application, that'd be basically what you're after.
Otherwise, have a Google for daemonisation (which involves forking the process a couple of times); from memory, there's even a decent Python library now (which, from memory, requires 2.5/2.6 with statements) which would help make that side of things easier :).

Related

Python echo the terminal outputs--no buffering

I'm trying to make a GUI. The program enables VNC on a device. So when I press GO button, it'll start with "Enabling VNC..." It takes a few seconds to enable. Followed by a "sucCess" message at upon completion. I see this in the terminal.
I'm trying to do the same thing on the GUI. The "enabling vnc" message doesn't even appear until the very end. It appears together with the "success" message. It's like it buffers all GUI output messages together then spits it all out in the end.
Here is what I have:
def actions():
# Enable VNC
if action_VNC.get():
print("Enabling VNC...")
msg_initVNC = "Enabling VNC..." + "\n"
outText.insert(tk.END, msg_initVNC)
#DO STUFF HERE
print('Success. VNC enabled.')
print('########')
msg_outVNC = 'Success. VNC enabled.' + "\n" + '########' +"\n"
outText.insert(tk.END, msg_outVNC)
window.mainloop()
The #DO STUFF HERE enables the VNC and it works. It' takes about 3 -5 seconds to happen. I'd like the program to output the print text to my GUI when it is supposed to and not wait until the end to output everything together. It does this already on the terminal. Thoughts?
I found window.update_idletasks() worked very well for me. So my output code looks like this:
outText.insert(tk.END, msg_seconds)
window.update_idletasks()
It's kinda buffered. GUIs work by "event driven" programming, which means that the visible window is not updated until a function is finished. This means you can't make functions in a GUI that take a long time to complete.
One way around this is to use the threading module.
from threading import Thread
def actions():
# Enable VNC
if action_VNC.get():
print("Enabling VNC...")
msg_initVNC = "Enabling VNC..." + "\n"
outText.insert(tk.END, msg_initVNC)
t = Thread(target=do_stuff, daemon=True)
t.start() # start the worker function in the background
root.bind("<<TaskDone>>", task_done) # listen for the task done event
def do_stuff():
#DO STUFF HERE
root.event_generate("<<TaskDone>>") # send the task done event
def task_done(*args):
print('Success. VNC enabled.')
print('########')
msg_outVNC = 'Success. VNC enabled.' + "\n" + '########' +"\n"
outText.insert(tk.END, msg_outVNC)
window.mainloop()

libvlc and dbus interface

I'm trying a to create a basic media player using libvlc which will be controlled through dbus. I'm using the gtk and libvlc bindings for python. The code is based on the official example from the vlc website
The only thing I modified is to add the dbus interface to the vlc instance
# Create a single vlc.Instance() to be shared by (possible) multiple players.
instance = vlc.Instance()
print vlc.libvlc_add_intf(instance, "dbus"); // this is what i added. // returns 0 which is ok
All is well, the demo works and plays any video files. but for some reason the dbus control module doesn't work (I can't believe I just said the dreaded "doesn't work" words):
I already have the working client dbus code which binds to the MPRIS 2 interface. I can control a normal instance of a VLC media player - that works just fine, but with the above example nothing happens. The dbus control module is loaded properly, since libvlc_add_intf doesn't return an error and i can see the MPRIS 2 service in D-Feet (org.mpris.MediaPlayer2.vlc).
Even in D-Feet, trying to call any of the methods of the dbus vlc object returns no error but nothing happens.
Do I need to configure something else in order to make the dbus module control the libvlc player?
Thanks
UPDATE
It seems that creating the vlc Instance and setting a higher verbosity, shows that the DBus calls are received but they have no effect whatsoever on the player itself.
Also, adding the RC interface to the instance instead of DBus, has some problems too: When I run the example from the command line it drops me to the RC interface console where i can type the control commands, but it has the same behaviour as DBus - nothing happens, no error, nada, absolutely nothing. It ignores the commands completely.
Any thoughts?
UPDATE 2
Here is the code that uses libvlc to create a basic player:
from dbus.mainloop.glib import DBusGMainLoop
import gtk
import gobject
import sys
import vlc
from gettext import gettext as _
# Create a single vlc.Instance() to be shared by (possible) multiple players.
instance = vlc.Instance("--one-instance --verbose 2")
class VLCWidget(gtk.DrawingArea):
"""Simple VLC widget.
Its player can be controlled through the 'player' attribute, which
is a vlc.MediaPlayer() instance.
"""
def __init__(self, *p):
gtk.DrawingArea.__init__(self)
self.player = instance.media_player_new()
def handle_embed(*args):
if sys.platform == 'win32':
self.player.set_hwnd(self.window.handle)
else:
self.player.set_xwindow(self.window.xid)
return True
self.connect("map", handle_embed)
self.set_size_request(640, 480)
class VideoPlayer:
"""Example simple video player.
"""
def __init__(self):
self.vlc = VLCWidget()
def main(self, fname):
self.vlc.player.set_media(instance.media_new(fname))
w = gtk.Window()
w.add(self.vlc)
w.show_all()
w.connect("destroy", gtk.main_quit)
self.vlc.player.play()
DBusGMainLoop(set_as_default = True)
gtk.gdk.threads_init()
gobject.MainLoop().run()
if __name__ == '__main__':
if not sys.argv[1:]:
print "You must provide at least 1 movie filename"
sys.exit(1)
if len(sys.argv[1:]) == 1:
# Only 1 file. Simple interface
p=VideoPlayer()
p.main(sys.argv[1])
the script can be run from the command line like:
python example_vlc.py file.avi
The client code which connects to the vlc dbus object is too long to post so instead pretend that i'm using D-Feet to get the bus connection and post messages to it.
Once the example is running, i can see the players dbus interface in d-feet, but i am unable to control it. Is there anything else that i should add to the code above to make it work?
I can't see your implementation of your event loop, so it's hard to tell what might be causing commands to not be recognized or to be dropped. Is it possible your threads are losing the stacktrace information and are actually throwing exceptions?
You might get more responses if you added either a psuedo-code version of your event loop and DBus command parsing or a simplified version?
The working programs found on nullege.com use ctypes. One which acted as a server used rpyc. Ignoring that one.
The advantages of ctypes over dbus is a huge speed advantage (calling the C library code, not interacting using python) as well as not requiring the library to implement the dbus interface.
Didn't find any examples using gtk or dbus ;-(
Notable examples
PyNuvo vlc.py
Milonga Tango DJing program
Using dbus / gtk
dbus uses gobject mainloop, not gtk mainloop. Totally different beasts. Don't cross the streams! Some fixes:
Don't need this. Threads are evil.
gtk.gdk.threads_init()
gtk.main_quit() shouldn't work when using gobject Mainloop. gobject mainloop can't live within ur class.
if __name__ == '__main__':
loop = gobject.MainLoop()
loop.run()
Pass in loop into ur class. Then call to quit the app
loop.quit()
dbus (notify) / gtk working example
Not going to write ur vlc app for u. But here is a working example of using dbus / gtk. Just adapt to vlc. Assumed u took my advise on gtk above. As u know any instance of DesktopNotify must be called while using gobject.Mainloop . But u can place it anywhere within ur main class.
desktop_notify.py
from __future__ import print_function
import gobject
import time, dbus
from dbus.exceptions import DBusException
from dbus.mainloop.glib import DBusGMainLoop
class DesktopNotify(object):
""" Notify-OSD ubuntu's implementation has a 20 message limit. U've been warned. When queue is full, delete old message before adding new messages."""
#Static variables
dbus_loop = None
dbus_proxy = None
dbus_interface = None
loop = None
#property
def dbus_name(self):
return ("org.freedesktop.Notifications")
#property
def dbus_path(self):
return ("/org/freedesktop/Notifications")
#property
def dbus_interface(self):
return self.dbus_name
def __init__(self, strInit="initializing passive notification messaging")
strProxyInterface = "<class 'dbus.proxies.Interface'>"
""" Reinitializing dbus when making a 2nd class instance would be bad"""
if str(type(DesktopNotify.dbus_interface)) != strProxyInterface:
DesktopNotify.dbus_loop = DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus(mainloop=DesktopNotify.dbus_loop)
DesktopNotify.dbus_proxy = bus.get_object(self.dbus_name, self.dbus_path)
DesktopNotify.dbus_interface = dbus.Interface(DesktopNotify.dbus_proxy, self.dbus_interface )
DesktopNotify.dbus_proxy.connect_to_signal("NotificationClosed", self.handle_closed)
def handle_closed(self, *arg, **kwargs):
""" Notification closed by user or by code. Print message or not"""
lngNotificationId = int(arg[0])
lngReason = int(arg[1])
def pop(self, lngID):
""" ID stored in database, but i'm going to skip this and keep it simple"""
try:
DesktopNotify.dbus_interface.CloseNotification(lngID)
except DBusException as why:
print(self.__class__.__name__ + ".pop probably no message with id, lngID, why)
finally:
pass
def push(self, strMsgTitle, strMsg, dictField):
""" Create a new passive notification (took out retrying and handling full queues)"""
now = time.localtime( time.time() )
strMsgTime = strMsg + " " + time.asctime(now)
del now
strMsgTime = strMsgTime % dictField
app_name="[your app name]"
app_icon = ''
actions = ''
hint = ''
expire_timeout = 10000 #Use seconds * 1000
summary = strMsgTitle
body = strMsgTime
lngNotificationID = None
try:
lngNotificationID = DesktopNotify.dbus_interfacec.Notify(app_name, 0, app_icon, summary, body, actions, hint, expire_timeout)
except DBusException as why:
#Excellent spot to delete oldest notification and then retry
print(self.__class__.__name__ + ".push Being lazy. Posting passive notification was unsuccessful.", why)
finally:
#Excellent spot to add to database upon success
pass

How to capture frames from Apple iSight using Python and PyObjC?

I am trying to capture a single frame from the Apple iSight camera built into a Macbook Pro using Python (version 2.7 or 2.6) and the PyObjC (version 2.2).
As a starting point, I used this old StackOverflow question. To verify that it makes sense, I cross-referenced against Apple's MyRecorder example that it seems to be based on. Unfortunately, my script does not work.
My big questions are:
Am I initializing the camera correctly?
Am I starting the event loop correctly?
Was there any other setup I was supposed to do?
In the example script pasted below, the intended operation is that after calling startImageCapture(), I should start printing "Got a frame..." messages from the CaptureDelegate. However, the camera's light never turns on and the delegate's callback is never executed.
Also, there are no failures during startImageCapture(), all functions claim to succeed, and it successfully finds the iSight device. Analyzing the session object in pdb shows that it has valid input and output objects, the output has a delegate assigned, the device is not in use by another processes, and the session is marked as running after startRunning() is called.
Here's the code:
#!/usr/bin/env python2.7
import sys
import os
import time
import objc
import QTKit
import AppKit
from Foundation import NSObject
from Foundation import NSTimer
from PyObjCTools import AppHelper
objc.setVerbose(True)
class CaptureDelegate(NSObject):
def captureOutput_didOutputVideoFrame_withSampleBuffer_fromConnection_(self, captureOutput,
videoFrame, sampleBuffer,
connection):
# This should get called for every captured frame
print "Got a frame: %s" % videoFrame
class QuitClass(NSObject):
def quitMainLoop_(self, aTimer):
# Just stop the main loop.
print "Quitting main loop."
AppHelper.stopEventLoop()
def startImageCapture():
error = None
# Create a QT Capture session
session = QTKit.QTCaptureSession.alloc().init()
# Find iSight device and open it
dev = QTKit.QTCaptureDevice.defaultInputDeviceWithMediaType_(QTKit.QTMediaTypeVideo)
print "Device: %s" % dev
if not dev.open_(error):
print "Couldn't open capture device."
return
# Create an input instance with the device we found and add to session
input = QTKit.QTCaptureDeviceInput.alloc().initWithDevice_(dev)
if not session.addInput_error_(input, error):
print "Couldn't add input device."
return
# Create an output instance with a delegate for callbacks and add to session
output = QTKit.QTCaptureDecompressedVideoOutput.alloc().init()
delegate = CaptureDelegate.alloc().init()
output.setDelegate_(delegate)
if not session.addOutput_error_(output, error):
print "Failed to add output delegate."
return
# Start the capture
print "Initiating capture..."
session.startRunning()
def main():
# Open camera and start capturing frames
startImageCapture()
# Setup a timer to quit in 10 seconds (hack for now)
quitInst = QuitClass.alloc().init()
NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(10.0,
quitInst,
'quitMainLoop:',
None,
False)
# Start Cocoa's main event loop
AppHelper.runConsoleEventLoop(installInterrupt=True)
print "After event loop"
if __name__ == "__main__":
main()
Thanks for any help you can provide!
OK, I spent a day diving through the depths of PyObjC and got it working.
For future record, the reason the code in the question did not work: variable scope and garbage collection. The session variable was deleted when it fell out of scope, which happened before the event processor ran. Something must be done to retain it so it is not freed before it has time to run.
Moving everything into a class and making session a class variable made the callbacks start working. Additionally, the code below demonstrates getting the frame's pixel data into bitmap format and saving it via Cocoa calls, and also how to copy it back into Python's world-view as a buffer or string.
The script below will capture a single frame
#!/usr/bin/env python2.7
#
# camera.py -- by Trevor Bentley (02/04/2011)
#
# This work is licensed under a Creative Commons Attribution 3.0 Unported License.
#
# Run from the command line on an Apple laptop running OS X 10.6, this script will
# take a single frame capture using the built-in iSight camera and save it to disk
# using three methods.
#
import sys
import os
import time
import objc
import QTKit
from AppKit import *
from Foundation import NSObject
from Foundation import NSTimer
from PyObjCTools import AppHelper
class NSImageTest(NSObject):
def init(self):
self = super(NSImageTest, self).init()
if self is None:
return None
self.session = None
self.running = True
return self
def captureOutput_didOutputVideoFrame_withSampleBuffer_fromConnection_(self, captureOutput,
videoFrame, sampleBuffer,
connection):
self.session.stopRunning() # I just want one frame
# Get a bitmap representation of the frame using CoreImage and Cocoa calls
ciimage = CIImage.imageWithCVImageBuffer_(videoFrame)
rep = NSCIImageRep.imageRepWithCIImage_(ciimage)
bitrep = NSBitmapImageRep.alloc().initWithCIImage_(ciimage)
bitdata = bitrep.representationUsingType_properties_(NSBMPFileType, objc.NULL)
# Save image to disk using Cocoa
t0 = time.time()
bitdata.writeToFile_atomically_("grab.bmp", False)
t1 = time.time()
print "Cocoa saved in %.5f seconds" % (t1-t0)
# Save a read-only buffer of image to disk using Python
t0 = time.time()
bitbuf = bitdata.bytes()
f = open("python.bmp", "w")
f.write(bitbuf)
f.close()
t1 = time.time()
print "Python saved buffer in %.5f seconds" % (t1-t0)
# Save a string-copy of the buffer to disk using Python
t0 = time.time()
bitbufstr = str(bitbuf)
f = open("python2.bmp", "w")
f.write(bitbufstr)
f.close()
t1 = time.time()
print "Python saved string in %.5f seconds" % (t1-t0)
# Will exit on next execution of quitMainLoop_()
self.running = False
def quitMainLoop_(self, aTimer):
# Stop the main loop after one frame is captured. Call rapidly from timer.
if not self.running:
AppHelper.stopEventLoop()
def startImageCapture(self, aTimer):
error = None
print "Finding camera"
# Create a QT Capture session
self.session = QTKit.QTCaptureSession.alloc().init()
# Find iSight device and open it
dev = QTKit.QTCaptureDevice.defaultInputDeviceWithMediaType_(QTKit.QTMediaTypeVideo)
print "Device: %s" % dev
if not dev.open_(error):
print "Couldn't open capture device."
return
# Create an input instance with the device we found and add to session
input = QTKit.QTCaptureDeviceInput.alloc().initWithDevice_(dev)
if not self.session.addInput_error_(input, error):
print "Couldn't add input device."
return
# Create an output instance with a delegate for callbacks and add to session
output = QTKit.QTCaptureDecompressedVideoOutput.alloc().init()
output.setDelegate_(self)
if not self.session.addOutput_error_(output, error):
print "Failed to add output delegate."
return
# Start the capture
print "Initiating capture..."
self.session.startRunning()
def main(self):
# Callback that quits after a frame is captured
NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(0.1,
self,
'quitMainLoop:',
None,
True)
# Turn on the camera and start the capture
self.startImageCapture(None)
# Start Cocoa's main event loop
AppHelper.runConsoleEventLoop(installInterrupt=True)
print "Frame capture completed."
if __name__ == "__main__":
test = NSImageTest.alloc().init()
test.main()
QTKit is deprecated and PyObjC is a big dependency (and seems to be tricky to build if you want it in HomeBrew). Plus PyObjC did not have most of AVFoundation so I created a simple camera extension for Python that uses AVFoundation to record a video or snap a picture, it requires no dependencies (Cython intermediate files are committed to avoid the need to have Cython for most users).
It should be possible to build it like this:
pip install -e git+https://github.com/dashesy/pyavfcam.git
Then we can use it to take a picture:
import pyavfcam
# Open the default video source
cam = pyavfcam.AVFCam(sinks='image')
frame = cam.snap_picture('test.jpg') # frame is a memory buffer np.asarray(frame) can retrieve
Not related to this question, but if the AVFCam class is sub-classed, the overridden methods will be called with the result.

How do I get all instances of VLC on dbus quickly?

basically the problem is, that the only way to get all instances of VLC is to search all non-named instances for the org.freedesktop.MediaPlayer identity function and call it.
(alternatively I could use the introspection API, but this wouldn't seem to solve my problem)
Unfortunately many programs upon having sent a dbus call, simply do not respond, causing a long and costly timeout.
When this happens multiple times it can add up.
Basically the builtin timeout is excessively long.
If I can decrease the dbus timeout somehow that will solve my problem, but the ideal solution would be a way.
I got the idea that I could put each call to "Identify" inside a thread and that I could kill threads that take too long, but this seems not to be suggested. Also adding multithreading greatly increases the CPU load while not increasing the speed of the program all that much.
here is the code that I am trying to get to run quickly (more or less) which is currently painfully slow.
import dbus
bus = dbus.SessionBus()
dbus_proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
names = dbus_proxy.ListNames()
for name in names:
if name.startswith(':'):
try:
proxy = bus.get_object(name, '/')
ident_method = proxy.get_dbus_method("Identity",
dbus_interface="org.freedesktop.MediaPlayer")
print ident_method()
except dbus.exceptions.DBusException:
pass
Easier than spawning a bunch of threads would be to make the calls to the different services asynchronously, providing a callback handler for when a result comes back or a D-Bus error occurs. All of the calls effectively happen in parallel, and your program can proceed as soon as it gets some positive results.
Here's a quick-and-dirty program that prints a list of all the services it finds. Note how quickly it gets all the positive results without having to wait for any timeouts from anything. In a real program you'd probably assign a do-nothing function to the error handler, since your goal here is to ignore the services that don't respond, but this example waits until it's heard from everything before quitting.
#! /usr/bin/env python
import dbus
import dbus.mainloop.glib
import functools
import glib
class VlcFinder (object):
def __init__ (self, mainloop):
self.outstanding = 0
self.mainloop = mainloop
bus = dbus.SessionBus ()
dbus_proxy = bus.get_object ("org.freedesktop.DBus", "/org/freedesktop/DBus")
names = dbus_proxy.ListNames ()
for name in dbus_proxy.ListNames ():
if name.startswith (":"):
proxy = bus.get_object (name, "/")
iface = dbus.Interface (proxy, "org.freedesktop.MediaPlayer")
iface.Identity (reply_handler = functools.partial (self.reply_cb, name),
error_handler = functools.partial (self.error_cb, name))
self.outstanding += 1
def reply_cb (self, name, ver):
print "Found {0}: {1}".format (name, ver)
self.received_result ()
def error_cb (self, name, msg):
self.received_result ()
def received_result (self):
self.outstanding -= 1
if self.outstanding == 0:
self.mainloop.quit ()
if __name__ == "__main__":
dbus.mainloop.glib.DBusGMainLoop (set_as_default = True)
mainloop = glib.MainLoop ()
finder = VlcFinder (mainloop)
mainloop.run ()

Python threading question - returning control to parent

Basically, I have a python program which listens for DeviceAdded DBus events (e.g. when someone plugs in a USB drive), and when an event occurs, I want to create a thread which collects metadata on that newly connected device. However, I want to do this asynchronously - that is, allow one thread to keep collecting metadata on the device while returning control to the parent which can keep listening for these events. At the moment, my thread blocks until the collection is finished. Here is a sample of my code:
class DeviceAddedListener:
def __init__(self):
self.bus = dbus.SystemBus()
self.hal_manager_obj = self.bus.get_object("org.freedesktop.Hal", "/org$
self.hal_manager = dbus.Interface(self.hal_manager_obj, "org.freedeskto$
self.hal_manager.connect_to_signal("DeviceAdded", self._filter)
def _filter(self, udi):
device_obj = self.bus.get_object ("org.freedesktop.Hal", udi)
device = dbus.Interface(device_obj, "org.freedesktop.Hal.Device")
if device.QueryCapability("volume"):
return self.capture(device)
def capture(self,volume):
self.device_file = volume.GetProperty("block.device")
self.label = volume.GetProperty("volume.label")
self.fstype = volume.GetProperty("volume.fstype")
self.mounted = volume.GetProperty("volume.is_mounted")
self.mount_point = volume.GetProperty("volume.mount_point")
try:
self.size = volume.GetProperty("volume.size")
except:
self.size = 0
print "New storage device detected:"
print " device_file: %s" % self.device_file
print " label: %s" % self.label
print " fstype: %s" % self.fstype
if self.mounted:
print " mount_point: %s" % self.mount_point
response = raw_input("\nWould you like to acquire %s [y/N]? " % self.device_file)
if (response == "y"):
self.get_meta()
thread.start_new_thread(DoSomething(self.device_file))
else:
print "Returning to idle"
if __name__ == '__main__':
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
loop = gobject.MainLoop()
DeviceAddedListener()
loop.run()
Any thoughts would be greatly appreciated :) I have excluded the import list to save space
Try spawning a thread just for the capture stuff, by changing the following lines in your _filter() function to this:
if device.QueryCapability("volume"):
threading.start_new_thread(self.capture, (device))
This is assuming that the bulk of the work is happening in the capture() function. If not, then just spawn the thread a little earlier, possibly on the whole _filter() function.
This should then spawn a new thread for every filtered device detected. Bear in mind that I haven't done any dbus stuff and can't really test this, but it's an idea.
Also, you're trying to get user input from the capture function which, using the app as you've defined it, isn't really a nice thing to do in threads. What if a second device is connected while the first prompt is still on screen? Might not play nicely.
The design of this thing might be exactly the way you want it for specific reasons, but I can't help feeling like it could be a lot slicker. It's not really designed with threads in mind from what I can tell.

Categories

Resources