A few days ago someone said something that reminded me about my audio player, which I had abandoned for more than a year already. The reason was mostly that the two Phonon backends, VLC and gstreamer, for some reason or other couldn't play the files I had without any gaps between songs.
To be honest, the first bug end up being me not properly encoding the filenames.
If you first URL-encoded the filename and then built a Q/KURL with that, then
it's all fine. It took me more than 12 months and a few rereads of the thread
to realize it. Fixes apart, it seems that the bug still exists for other
instances of gstreamer errors, so we're not out of the woods. In any case, I
switched to the VLC backend and it seems that now is able to fire the
aboutToFinnish() signal properly, so for the moment I'm using that.
All that is fine, but that's not what I wanted to talk about in this post. Given
that this project largely precedes my interest on testing, it has no testing at
all. Most of the project is straightforward enough to almost no need any, but
there's a critic part that would not suffer at all if it had any, namely the
Collections handling, including passing files from one to another and
automatically updating new/removed
So after fixing the bug mentioned above I tried to figure out the current state
of affaires regarding
Collections, and boy, they're in bad shape. The code was
locally modified, never commited, deactivating any notifications of filesystem
changes (new or removed files), and other code I can't really understand the
Because if this last detail is that I decided to start testing three classes:
Collection, which handles a set a
Songs with a common root directory;
CollectionAggregator, which handles a set of collections and should coordinate
Song from one
Collection to another; and
CollectionIndexer, which scans
Collection's root dir to find
All went fine while I tested the first class, Collection. There was a tricky
part where I had to setup a
QApplication in order to make signals work. The
problems began when I started testing
CollectionIndexer. Tests started blocking
endlessly, signals stopped being either emited or firing the connected slots,
life was bad.
I tried to search the available documentation and mailing lists for a hint about the problem, but besides a quite complex example that didn't seem to properly converge to anything useful, I was mostly on my own.
This morning I got my eureka moment: I noticed that if I executed each test class
by itself, it worked, but both at the same time blocked and never finished. Then
I remembered something said in
For any GUI application using Qt, there is precisely one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time.
That was it: I was creating the application, first in the
setUp() method, then
as a class attribute, but I had one test class per class to test, each in its
own file. Somehow this last fact lead me to think that somehow they were executed
in separate processes, which is not true. Luckily, even with this limitation, there's
none on the amount of times you can
quit() the same instance, so
that's what I did: I created only one instance and reused it everywhere. I was
already doing that for each test method, but again, somehow having several files
mislead me to think they were isolated from each other.
So now all my unit tests work without mysteriously blocking forever. Now I just
hope I can keep riding the success wave and bring
satyr into good shape. A new
release wouldn't hurt.
 No matter how much I try, I can't get any vaguer.
 Ok, maybe the
Playlist combo wouldn't hurt to have UTs either.
After several months thinking about it, and just two requests, I finally decided
satyr's code. I decided to use github because I already switched to
git, mainly for testing and understanding it. I think I
can live with
hg, althought branch management in
git seems to be given more
thought and a good implementation.
So, without further ado: satyr in github
Remember, it's still a test software, by no means polished or ready for human
consumption, and with very low development force. Still, I think it has some
nice features, like interchangeable skins and a well defined backend,
support, quick tag edition, reasonable collection managment, and, thanks to
Phonon, almost-gapless playback and things like «stop after playing the
current this file» (but not «after any given file» yet).
In Debian Sid it mostly works only with the
GStreamer backend; I haven't tried
xine one and I know
VLC does not emit a signal needed for queueing the
next song, so you have to press «next» after each song. AFAIK this is fixed
satyr-0.3.2 "I should install my own food" is out. The Changelog is not very impressive:
- We can save the list of queued Songs on exit and load it at startup.
- The queue position is not presented when editing the title of a Song that is queued.
- Setting the track number works now (was a bug).
- Fixed a bug in setup.py.
but the last one is somewhat important (Thanks Chipaca). Also, 2 months ago, I made the satyr-0.3.1 "freudian slip" release, when user 'dglent' from http://kde-apps.org/ found a packaging bug. It was not only a bugfixing revision, it also included new features:
- nowPlaying(), exported via dbus.
- 'now playing' plugin for irssi.
- Changes in tags is copied to selected cells (in the same column). This allows 'Massive tag writing'.
- Fixed "NoneType has no attribute 'row'" bug, but this is just a workaround.
- Forgot to install complex.ui.
Now go get it!
One of the features I planned for
satyr almost since the begining was the
possibility to have 'skins'. In this context, a skin would not only implement
the look and feel, but also could implement features the weren't available in
the shipped classes. I also planned to implement this feature after I had most of
the others one already done. But then I was bored this weekend with nothing to do
and I decided to set off to at least investigate how to do it. Of course, what
happened was that I implemented it almost completely.
Up to now,
satyr's user interface was implemented in two files:
which was compiled with
default.py, and some code in
satyr.py itself. This
of course would not scale, and I always had the idea of moving the behaviour
satyr.py to a file called
default.py and load the ui directly
default.ui file without compiling, getting rid of the need for a
compilation at the same time. This also meant that then a skin would
consist of a
.py file and possibly a
.ui file. There are three problems to
solve for this: getting the local-to-the-user's skin directory, loading the skin
and loading the correspondant
The first part is simple from the
PyKDE4 point of view:
# get the app's dir; don't forget the trailing '/'! appDir= KStandardDirs.locateLocal ('data', 'satyr/')
I'll first explain the other two parts, loading the skin and the
before returning more deeply to the consequences of this solution.
I put all the skins in a
skins subdirectory. To make
it a proper python module I added an empty
__init__.py file. Now, I could
import skins.<skinName> and possibly instantiate some class in it, but
of course one cannot write that. I could resort to
eval ('import skins.'+skinName), but we know that
eval() has the most
long-standing typo in the history of computer languages, and it's actually
What we can do is resort to
__import__() instead. This little function does
approximately what we want. I say approx because it has some surprises in the
sleeves of its sleeveless code. I suggest you to go read carefully its
documentation. Meanwhile, the magic itself:
mod= __import__ ('skins.'+skinName, globals(), locals(), 'MainWindow') mw= mod.MainWindow ()
.ui file in the skin's code is rather simple: just get the skin
module's filepath, replace
.ui, and load it with
PyQt4.uic.loadUiType(). This function returns a generated class for the
topmost widget and its Qt base class. This generated class has a
method that is the one that actually builds de UI.
So, we just instantiate the main window's class and call its
from import uic # !!! __file__ might end with .py[co]! uipath= __file__[:__file__.rfind ('.')]+'.ui' # I don't care about the base class (UIMainWindow, buh)= uic.loadUiType (uipath) self.ui= UIMainWindow () self.ui.setupUi (self)
Note the comment about the
__file__ attribute of a module.
Now, and back to the first part, finding the local-to-the-user's skin directory is the easiest part. From there, things get a little bit more complicated:
skinssubdirectory might not exist.
- If you create it, you gotta make sure to also throw in a
- Once you've done it, you also need to add the local-to-the-user's app
directory to the path. It's easy, just prepend it to
sys.path, so it's used before any system-wide directory.
- The last problem that remains is exactly that: once the
__init__.pyis there and the user's dir is prepended to
sys.path, the user's local skin directory is always used when importing anything from the
skinsmodule, so if a skin is not there it is not loadable. All skins distributed with
satyrwill be inaccesible!
So I'm in a kind of dead alley here. I have a couple of ideas on how to work-around this, but they're at best hacky, and I don't want to implement them until I'm sure that it's inevitable.
 Not a very happy name, if you ask me.
 Very similar to what you get if you compile the
I seem to have fixed the bug I mentioned in my last post. This is what I had:
class <span class="createlink">MetaPlayer</span> (type (QObject), type (dbus.service.Object)): """Dummy metaclass that allows us to inherit from both QObject and d.s.Object""" pass class Player (QObject, dbus.service.Object): __metaclass__= <span class="createlink">MetaPlayer</span> [...]
MetaPlayer doesn't have a explicit
__init__() method; one
would spect that Python would take are of that. Here's the fixing code:
<span class="createlink">MetaQObject</span>= type (QObject) <span class="createlink">MetaObject</span>= type (dbus.service.Object) class <span class="createlink">MetaPlayer</span> (MetaQObject, <span class="createlink">MetaObject</span>): """Dummy metaclass that allows us to inherit from both QObject and d.s.Object""" def __init__(cls, name, bases, dct): <span class="createlink">MetaObject</span>.__init__ (cls, name, bases, dct) <span class="createlink">MetaQObject</span>.__init__ (cls, name, bases, dct)
I really don't understand why I have to be so explicit. Maybe it's because
the metaclass for
inherits from the
type type; this type is a new style class, but
doesn't inherits from
object. Thus, I think, the inherited
methods are not called automatically.
In any case, now I can mix
dbus.service.Object, and it works
fine. For instance, this call works:
$ qdbus org.kde.satyr /player quit
type type is of type
In : type (type) Out: <type 'type'>
 its type is not
type, as mentioned above.
I certainly hope this is the last post in the
Phonon-and-badly-encoded/mixed-encodings-filenames saga, but I know is just
wishful thinking: as all encoding-related problems they never really dissapear,
it's just that you hadn't hit the right wrong stone yet. In any case, I fixed
all my later problems wherever they where, and now I can answer this question:
how to play files whose filenames are badly encoded and/or have mixed
encodings, all this in
Right now the answer is: you have to provide a properly encoded
QUrl. How, you
might ask, can I get one of those? Are they selled in the same odly-looking
places where you can buy cigarretes, or even marihuana? The answer, luckly,
is way more simple.
Putting together all the code I've been showing about
Phonon recently, it comes down to this:
# path is a str() qba= QByteArray (path) # the exceptions are not needed, # but is cleaner if you print the outcome of this qu= QUrl.fromEncoded (qba.toPercentEncoding ("/ ")) # this is needed by the gstreamer backend, # and the xine backend doesn't complain qu.setScheme ('file')
... and that's it. You can now create a
MediaSource with this
There are a couple of ideas that I want to express as conclusion to all this:
- In an ideal world these things should not happen. But this is one of the lesser problems with this non-ideal world, so bear with it.
- Paths should not be stored in
QStrings, even if they can (and they do) store this kind of pathnames, because if you try to 'encode' its contents (in the Unicode sense; that is, convert it to an encoding like UTF-8) you get farts or barks at best. Yes, you always have
QString's class reference there is no warranty that this will keep being the case.
- In fact,
QString's class reference says at some point: «[one case] where
QByteArrayis appropriate are when you need to store raw binary data...», and [as I already wrote](http://grulicueva.homelinux.net/~mdione/glob/posts/from-qstring-to-bytes- in-pyqt/), «[t]his would be the case for paths; you need the bytes».
QDircan only be created from
QStrings. I'm not sure if, given all I wrote, that's right.
The good news is that
satyr now can play any file that the backends can
whatever their filename-as-string-of-bytes is, I'm a little bit happier about
I got another contribution to
KDE and might even have to close a lot of bugs!
 That question is only legal in Nederlands and very few others cities in the planet.
 Actually is not legal. See [this wikipedia article](http://en.wikipedia.org/wiki/Drug_policy_of_the_Netherlands#Non- enforcement).
 I might pull up my sleeves again and fix that.
 You might have already know this, but if you not: you cannot print Unicode,
because Unicode is not and encoding. You have to encode it first. Hence, the
toUtf8() and similar
QString methods, and also the inverse
 Of course the equivalent C++ code also works, with
path being a
 And in the case of
PyQt4, that method is not even available. But [I
For a couple of months I've been globbing about
satyr (it's name should always be
written in lowercase) should have the following features:
- The and the Collection(s) are the same thing.
- Yours is a Collection of Albums, nothing else.
- Some Albums are from the same artists and some are compilations.
- If you want an ephemeral playlist you could queue songs.
- If you want non-ephemeral playlists, then this player is not for you.
- Ability to search à la
xmms, but in the same interface
- Tag reading and writing.
- Order you collection based on the tags.
- The collection discovers new files and adds them to the playlist on the fly.
- Be able to use all the program only with your keyboard (die, mouse, die!)
This and other features should be available soon. The coding has been fast
lately, mainly because the Qt/KDE libs are fantastic to work with. The only
thing I couldn't do was to read the tags before playing them, so I relied
The project is hosted in
savannah, and right now there
is no tarball (It's marked as alpha state because I sent a couple of
tarballs to some friends who asked for them), so the only way right now is
to branch anonimously the
repo. I hope you download and
enjoy it as much as I do.
 The support for several collections is not complete yet.
 Functionality available via dbus only at the moment.
 This is with the current GUI. I'm also thinking in several/pluginable GUI's
 Not yet available.
 Of course this only works if it's running. Otherwise, you can always ask for a rescanning.
kReiSSy (se pronuncia como la palabra "crazy" del inglés) es uno de mis proyectos más ambiciosos en este momento (tengo uno mayor, pero está en el freezer; ya hablaré de él). en resumen, puedo decir que kReiSSy es un lector de feeds (rss, atom, quéno) alimentado a gofios, o como dicen ahora, en esteroides. otra descripción puede ser: es un concentrador de información externa con categorzación basada en tags.
¿qué features lo hacen tan pulenta? veamos:
- tagging de posts (eso lo hace cualquiera).
- almacenamiento local de posts (en una base de datos, no borra ninguno).
- browser integrado. acá empecé a irme al carajo.
- tagging de páginas "leídas por ahí", que las convierte en first class citizens del programa.
¿qué significan estos dos últimos fatures? que puedo estar leyendo un post, seguir un link, de ahí a otro, y otro, todo en el browser integrado, y así hasta que encuentro otra página que nada que ver por dónde empecé, y entonces puedo tagear dicha página. esto hace dos cosas automáticas: guarda la página en la base de datos como si fuera un post más, y me permite luego buscar dicha página por tag.
ahora, me fui de boca un poco al decir "browser integrado" (esta es la sección "proyect status"). por ahora el browser nos permite ir hacia adelante, siguiendo links, pero no para atrás. es decir, le faltan todos los botones de navegación. también se podría marcar un post como "leer después", pero no hay código que lo haga. el filtrado por varios feeds o varios tags está roto/no anda (esto se debe o a un bug en SQLAlchemy o a mi inoperancia). aún no graba bien su sesión, cosa de volver en el mismo estado en que lo dejamos. y hace un par de chanchadas con los archivos, asume un par de paths y negradas así.
¿y en qué está hecha semejante bestia? python, obvio, mi lenguaje de cabecera desde hace unos añitos ya. pero no python puro, sino con varios agregados.
uno ya lo mencioné, SQLAlchemy, un ORM bastante potente, pues no fuerza muchas cosas. para usarlo como un ORM clásico (una clase por tabla) hay que hacer un par de giladas, pero nada grave. y permite hacer queries con SQL a lo macho, aunque no me llevo muy bien con eso. sqlite por debajo.
y la vedette de todos, y el motivo por el que empecé este proyecto: PyKDE. soy un usuario y fanático de kde desde que usé un redhat5.2 allá por el '98 o así. conozco bastante la infraestructura que hay por debajo, he leído varias veces cachos de código en busca de solucionar algún bug que me mordió, algunas veces hasta logré repararlo y todo. si bien su look no es muy bonito, la infraestructura que hay debajo es impresionante.
a tal punto que hacer este programa me resultó muy sencillo hasta ahora, pues sólo me concentré en mi funcionalidad. la parte de mostrado de html u otro tipo de archivos se lo dejé a KDE:
mime= KMimeType.findByURL(url, 0, False, False) mimeType= mime.name () if mimeType=='application/octet-stream': mimeType= KIO.NetAccess.mimetype (url, self); else: # asumo que es html mimeType= "text/html" ptr= KTrader.self().query(mimeType, "'KParts/ReadOnlyPart' in <span class="createlink">ServiceTypes</span>") part= createReadOnlyPart (ptr.library (), tab, ptr.name ())
esto hace la fantástica magia de fijarse quées el link (dado por url) y luego KTrader me entrega un KPart que sabe mostrar ese . simplemente la embebo en un tab y ya. juzguen ustedes.
ok, suficiente por ahora. ya estaré hablando de éste y otros proyectos.
 no es una traducción literal del "whatnot" en inglés, sino una reimplementación en castellano de la misma idea.
 uso muchos términos en inglés que ni me gasto en traducir. deal with it.
Update before even publishing: most of the numbers in the initial writing were
almost doubled. The problem was that
distutils left a
build directory when I
tried either to install or to package
satyr, I don't remember which, so the
files found by the
find commands below were mostly duplicated. I had to remove
the directory and run all the commands again!
I wanted to know some things about
satyr's code, in particular some statistics
about its lines of code. A first approach:
$ find . -name '*.py' -o -name '*.ui' | xargs wc -l | grep total 2397 total
Hmm, that's a lot, I don't remember wrinting so many lines. Beh, the comments, let's take them out:
$ find . -name '*.py' -o -name '*.ui' | xargs egrep -v '^#' | wc -l 2136
What about empty lines?:
$ find . -name '*.py' -o -name '*.ui' | xargs egrep -v '^(#.*| *)$' | wc -l 1764
Meeh, I didn't take out all the comment lines, only those lines starting
#, which are mainly the license lines on each source file. I have to also
count the comments in the middle of the code:
$ find . -name '*.py' -o -name '*.ui' | xargs egrep -v '^( *#.*| *)$' | wc -l 1475
And how much of those lines are actual code and not from some xml file describing user interface?:
$ find . -name '*.py' | xargs egrep -v '^( *#.*| *)$' | wc -l 1124
How much code means its 3 current skins?:
$ find satyr/skins/ -name '*.py' | xargs egrep -v '^( *#.*| *)$' | wc -l 341
How much in the most complex one?
$ egrep -v '^( *#.*| *)$' satyr/skins/complex.py | wc -l 182
All this numbers tell something: ~300 empty lines means that my code is not very tight. I already knew this: I like to break functions in secuential blocks of code, each one accomplishing a somehow atomic step towards the problem the function tries to solve. Almost 300 comment lines means my code is very well commented, even if a sixth of those comments are BUGs, TODOs, FIXMEs or HINTs:
$ find . -name '*.py' | xargs egrep '^ *# (TODO|BUG|FIXME|HINT)' | wc -l 56
Wow, more than I thought. Now, 1100 lines of actual code for all that
accomplishes today (playing almost any existing audio file format, progressive
collection scanning, lazy tag reading and also tag writing, 3 skins, handle
Collections, searching, random, stop after, saving some state, picking
up new songs added to the collection via the filesystem, queuing songs, dbus
interface, handle any encodings in filenames... phew! and some minor
features more!) I think is quite impressive.
Of course, doing all that in athousandsomething lines of code would be nearly
PyKDE4/KDE (something) 4,
Python itself. It's really nice to have such a nice framework to
work with, really.
 No user interface for this yet; shame on me.
 ... which toguether with
qdbus make my alarm clock.
 Almost all the support is in
  Less than 800 if we don't count skins.
 Yes, I add more footnotes as I readproof the posts :)
 I skipped  :)
 After the rebranding I don't know which is the proper name for the libraries, because I'm writing this post while very much offline, and TheDot does not publish the whole articles via rss, which I hate.
Satyr handles paths. There are some problems with paths and (sigh)
encondings. Of those, here are two: there's no way to know in which encoding
the filenames in a filesystem are enconded (f.i., there's no way to ask the
filesystem), and even if that were possible, the filenames might not even be
enconded in that enconding. In these (still!) transitioning times, lots and
lots and shitloads of filesystems are used in UTF-8 environments, but some
filenames are still in old ISO-8859-1 or whatever the system was using
QString. I'm taking a path from the command line; this path is
the location of the (right now only)
Collection for the player. I'm
handling the command line using
KCmdLineOptions, which returns
As we all know,
QString, just like the
unicode type in
all the data internally as Unicode, which is The Right Thing™. If you
really need the internal data, say as bytes, you can always call the
constData() method and be happy with it. This would be the case for paths;
you need the bytes.
PyQt4. For some reason, which maybe I will ask in the pyqt
constData() is not available. What to do? Well, that's what this post is
about. What you're about to read is hacky as it can be, but then it works. I
might feel dirty, but I can live with it. As long as I mark it as a
utter/über hack and promise to revert it once that's possible...
# path is a QString qba= QByteArray () qba.append (path) path= str (qba) # now path is a list of bytes/string.
Even if this part of the bug is fixed, then
Phonon.MediaObject.play() fails when feeded that same path with this
ERROR: backend <span class="createlink">MediaObject</span> reached <span class="createlink">ErrorState</span> after 1 . It seems a <span class="createlink">KioMediaStream</span> will not help here, trying anyway.
or simply refusing to continue. Sure, in my case I should simply ignore the filename and inform the user what's going on, but sometimes you can't be so gentle.
 but then it doesn't make much sense now since Phil wants to get rid of QString (for several reasons, which might most possibly include this one).