Schuerfen

Monday, December 14, 2009

Changing default editor for PDFs in GNOME

In Gnome (Gnome 2.28, Ubuntu 9.10), opening a file does not always yield the same result. Depending on where the file is double-clicked on, a different viewer might be displaying it. Here some idea how to get this right.

I wanted to open PDF files using Acrobat Reader instead of Ubuntu's default reader Evince. For Nautilus (and the Desktop), file associations can be changed using the file's property menu. This then resulted in PDF files to be opened with Acrobat Reader when double-clicked on in Nautilus and with Evince basically everywhere else: Firefox, Eclipse, ...

It turns out that Nautilus allows to specify custom associations, overriding Gnome's MIME-based system. So, to establish Acrobat Reader as the default editor for PDF files, open ~/.local/share/applications/defaults.list (i.e. in your home directory) and add the following entry to the [Default Applications] section.

application/pdf=AdobeReader.desktop

If there is no such file, do create it and paste the following content:

[Default Applications]
application/pdf=AdobeReader.desktop

Done. Double-clicking a PDF in Firefox now starts Acrobat Reader.

For inspiration on other mime types (and the global settings), see the file by the same name in /usr/share/applications.

Monday, October 12, 2009

FMC stencils for Dia

A friend and I are coding FMC stencils for Dia, which is Gnome's modeling tool to fight Visio. Looks pretty promising so far, and hopefully we'll get them finished in a month or so. Along with the actual stencils, we are producing a tad of documentation. It'll be made public somewhere, and if it is, I'll post a link here.

Friday, September 25, 2009

Private member variables in Python

Today, I stumbled upon the "Module Pattern" in the enlightening JavaScript: The Good Parts by Douglas Crockford. Using nested scopes and closures, one can have real private member variables for JavaScript. For the fun of it, I tried it with Python and it works, too. At least, kind of.

Have a look at the following piece of code. In it, we create an object of Quote. Since the class has been declared in a factory method, it has access to its local variables, "txt" in this case.

def create(arg):
txt = str(arg)

class Quote:
def get_txt(self):
return txt

return Quote()

quote = create("My tables! Meet it is, I set it down, ...")
print(dir(quote))
print(quote.get_txt())

Since it is not a variable of Quote itself, "txt" is not accessible from the outside. Yet, Python keeps those variables around, even after create has returned. The script delivers the following output:

['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_txt']
My tables! Meet it is, I set it down, ...

Now, what about changing the value? Let us add a simple setter method.

def create(arg):
txt = str(arg)

class Quote:
def get_txt(self):
return txt

def set_text(self, quote):
txt = quote

return Quote()

hamlet = create("There is something rotten in the state of Denmark.")
print(hamlet.get_txt())
hamlet.set_text("I'll make a ghost of him who lets me.")
print(hamlet.get_txt())

Seems straightforward enough, but results in the following undesired output.

There is something rotten in the state of Denmark.
There is something rotten in the state of Denmark.

Apparently, Python does not use lexical scoping when doing a name lookup for write access. Instead, it creates a local variable - which is discarded the same moment the setter-method returns.

Read access works though, and we can use this for a work-around by modifying objects instead of assigning values to names.

class Privates:
pass

def create(arg):
privates = Privates()
setattr(privates, "txt", str(arg))

class Quote:
def get_txt(self):
return privates.txt

def set_text(self, quote):
privates.txt = quote

return Quote()

hamlet = create("There is something rotten in the state of Denmark.")
print(hamlet.get_txt())
hamlet.set_text("I'll make a ghost of him who lets me.")
print(hamlet.get_txt())

This produces:

There is something rotten in the state of Denmark.
I'll make a ghost of him who lets me.

All things considered, this is pretty ugly. Python is all about openness, and privates are not. Also, the class definition is hidden because of a technical necessity. This is bad.

Summing it up: Nice that you can do it, but ... please don't.

(Full code here.)