Spanish only, since it's about a video in spanish ;-)
Acá está, gracias a la gente de Junín, un video de mi charla "Aplicaciones extensibles usando PyQt", en la que intento mostrar como desarrollar una aplicación con PyQt y yapsy.
No es una charla con la que esté muy contento. La otra salió mejor, pero no se filmó, así que quedará solo en la memoria de los cuatro gatos locos que estábamos ahí ;-)
That a plugin architecture for a complex app is a good idea is one of
those things that most people kinda agree on. One thing we don't
quite agree is how the heck are we going to make out app modular?
One way to do it (if you are coding python) is using Yapsy.
Yapsy is awesome. Also, yapsy is a bit underdocumented. Let's see
if this post fixes that a bit and leaves just the awesome.
Update: I had not seen the new Yapsy docs, released a few days ago. They are much better than what was there before :-)
Here's the general idea behind yapsy:
You create a Plugin Manager that can find and load plugins from a list
of places (for example, from ["/usr/share/appname/plugins",
"~/.appname/plugins"]).
A plugin category is a class.
There is a mapping between category names and category classes.
A plugin is a module and a metadata file. The module defines a
class that inherits from a category class, and belongs to that
category.
The metadata file has stuff like the plugin's name, description,
URL, version, etc.
One of the great things about Yapsy is that it doesn't specify too much.
A plugin will be just a python object, you can put whatever you want there,
or you can narrow it down by specifying the category class.
In fact, the way I have been doing the category classes is:
Start with an empty class
Implement two plugins of that category
If there is a chunk that's much alike in both, move it into
the category class.
But trust me, this will all be clearer with an example :-)
I will be doing it with a graphical PyQt app, but Yapsy works just as
well for headless of CLI apps.
Let's start with a simple app: an HTML editor with a preview widget.
Here's the code for the app, which is really simple (it doesn't save or do
anything, really, it's just an example):
But this application has an obvious limit: you have to type HTML in it. Why not
type python code in it and have it convert to HTML for display? Or Wiki markup,
or restructured text?
You could, in principle, just implement all those modes, but then you are assuming
the responsability of supporting every thing-that-can-be-turned-into-HTML. Your
app would be a monolith. That's where yapsy enters the scene.
So, let's create a plugin category, called "Formatter" which takes plain
text and returns HTML. Then we add stuff in the UI so the user can choose what
formatter he wants, and implement two of those.
Here's our plugin category class:
Of course what good is a plugin architecture without any plugins for it? So,
let's create two plugins.
First: a plugin that takes python code and returns HTML, thanks to pygments.
See how it goes into a plugins folder? Later on we will tell yapsy to search
there for plugins.
To be recognized as a plugin, it needs a metadata file, too:
And really, that's all there is to making a plugin. Here's another one
for comparison, which uses docutils to format reStructured Text:
And here they are in action:
Of course using categories you can do things like a "Tools" category, where the
plugins get added to a Tools menu, too.
And here's the application code:
In short: this is easy to do, and it leads to fixing your application's internal
structure, so it helps you write better code.
I have been putting lots of love into Aranduka an eBook manager, (which is looking very good lately, thanks!), and I didn't want it to also be an eBook reader.
But then I thought... how hard can it be to read ePub? Well, it's freaking easy!
Yes, it's yet another program I am working on. But hey, the last few I started are actually pretty functional already!
And... I am not doing this one alone, which should make it more fun.
It's an eBook (or just any book?) manager, that helps you keep your PDF/Mobi/FB2/whatever organized, and should eventually sync them to the device you want to use to read them.
You can get books from FeedBooks. Those books will get
downloaded, added to your database, tagged, the cover
fetched, etc. etc.
You can import your current folder of books in bulk.
Aranduka will use google and other sources to try to
guess (from the filename) what book that is and fill
in the extra data about it.
You can "guess" the extra data.
By marking certain data (say, the title) as reliable,
Aranduka will try to find some possible books that match
then you can choose if it's right.
Of course you can also edit that data manually.
And that's about it. Planned features:
Way too many to list.
The goals are clear:
It should be beautiful (I know it isn't!)
It should be powerful (not yet!)
It should be better than the "competition"
If those three goals are not achieved, it's failure. It may be a fun failure, but it would still be a failure.
Sometimes, you see a piece of code and it just feels right. Here's an example I found when doing my "Import Antigravity" session for PyDay Buenos Aires: the progressbar module.
Here's an example that will teach you enough to use progressbar effectively:
Yes, that's it, you will get a nice ASCII progress bar that goes across the terminal, supports resizing and moves as you iterate from 0 to 79.
The progressbar module even lets you do fancier things like ETA or fie transfer speeds, all just as nicely.
Isn't that code just right? You want a progress bar for that loop? Wrap it and you have one! And of course since I am a PyQt programmer, how could I make PyQt have something as right as that?
Here'show the output looks like:
You can do this with every toolkit, and you probably should!. It has one extra feature: you can interrupt the iteration.
Here's the (short) code:
# -*- coding: utf-8 -*-importsys,timefromPyQt4importQtCore,QtGuidefprogress(data,*args):it=iter(data)widget=QtGui.QProgressDialog(*args+(0,it.__length_hint__()))c=0forvinit:QtCore.QCoreApplication.instance().processEvents()ifwidget.wasCanceled():raiseStopIterationc+=1widget.setValue(c)yield(v)if__name__=="__main__":app=QtGui.QApplication(sys.argv)# Do something slowforxinprogress(xrange(50),"Show Progress","Stop the madness!"):time.sleep(.2)