A recurring problem in (not only) GUI programming are event cycles, i.e. the receiving of events oneself has triggered. These can quickly lead to event cycles, where change triggers change triggers change, until something gives out—usually the stack.
A particularly cheap way of breaking cycles are simple boolean flags:
class Foo(object):
listen = True
def some_method(self):
self.listen = False
try:
# do stuff that triggers some_event
finally:
self.listen = True
def on_some_event(self):
if not self.listen:
return
# handle event normally
Boolean flags are great. They’re simple, they’ll make your code harder and half of the time, you’ll forget to set them to True after you’re done. Or you forget the try...finally... block. Or you forget to check them in the event handler. What could possibly go wrong.
Fortunately, some GUI toolkits provide ways to temporarily disable event handling for specific events, like GObject’s handler_block_by_func. This approach has two problems:
- You have to know the object (or objects) that emits the event.
- It only works for GObject events (signals).
Since I do have classes with their own event handling mechanism, in order to be independent of GObject and since it’s not really difficult to implement, I wanted a cross-event-framework way of temporarily blocking event handling. Or, maybe I just wanted to write another decorator/context manager very badly. Maybe a little bit of both.
class Foo(object):
def some_method(self):
with self.on_some_event.suspend():
# do stuff that triggers some_event
@suspendable
def on_some_event(self):
# handle event normally
The event handlers are wrapped inside another method which swallows the event when the context manager is in suspended mode. The code remains blissfully ignorant of threading issues for now and also breaks down if the event handlers have meaningful result values. I’ve never encountered this so far, but adding default return values to the decorator is a simple extension. The methods are still proper bound methods and docstrings etc. are all conserved.
Without further ado, the code:
class Suspender(object):
def __init__(self):
self.is_suspended = 0
@contextmanager
def __call__(self):
self.is_suspended += 1
try:
yield
finally:
self.is_suspended -= 1
@classmethod
def suspendable(cls, meth):
suspend_manager = cls()
wrapper = suspend_manager.add_suspendable(meth)
wrapper.suspend = suspend_manager
wrapper.add_suspendable = suspend_manager.add_suspendable
return wrapper
def add_suspendable(self, meth):
@wraps(meth)
def suspended_wrapper(*args):
if self.is_suspended == 0:
meth(*args)
return suspended_wrapper
suspendable = Suspender.suspendable
It’s possible to have several event handlers block by a single context manager using the add_suspendable decorator added to suspendable methods:
@suspendable
def on_some_event(self): ...
@on_some_event.add_suspendable
def on_misc_event(self): ...
Calling the suspend() context manager for on_some_event will also block on_misc_event.
The context manager does not prevent the event from being propagated, so it’s not a speed optimization; due to the added boolean check event + call indirection, handling actually becomes slightly slower. It likely won’t make a difference. If it does, event cycles are the least of your problems.
Tags: python
Over the last months, I gave two talks, one at TaCoS in Heidelberg on Text+Berg digital, the other one in our local Python user group, swiss.py, on Python metaclasses.
The slides can be found on the Talks page.
Tags: Uncategorized
Tags: shnasel
Beginnings
After the bleak, joyless work of releasing software, plugging holes of preventing users from clicking buttons they should not have clicked in the first place, writing unctuous documentation and release notes and wrapping code into neat little installers with tiny bows and bells that gently tingle when touched, there are few things as profoundly satisfying as taking code that looked like line noise in the first place, applying some non-trivial transformation to it and still have it look like line noise, only now it’s twice as fast, or half as long or of some other inaccessible quality that can, on some technical level, be referred to as “cool”.
Quite some time ago, I decided that this time, “cool” will mean “runs on graphics hardware, possibly faster”. For this end, I took my trusty T61 to work, downloaded the latest CUDA toolkit, found out that nearly all examples crashed when compiled, installed the latest beta drivers from NVIDIA, noticed that everything worked now and started playing around with the examples.
After some time marveling at ball pit simulations and similarly telling examples, I printed out the CUDA programming guide and spent a considerable amount of time marveling at how well the NVidia green looked when printed with our institute’s color laser printer, before reading its more important parts.
[Read more →]
Tags: hardware · lang:en · programming
My Master thesis “Integration of Light-weight Semantics into a Syntax Query Formalism” is available from the SALSA project pages.
Abstract
In the Computational Linguistics community, much work is put into the creation of large, high-quality linguistic resources, often with complex annotation. In order to make these resources accessible to nontechnical audiences, formalisms for searching and filtering are needed.
The TIGER query language can, by describing partial structures, be used to search treebanks with syntactic annotation. Recently, augmented treebanks have been published, including the SALSA corpus which features frame semantic annotation on top of syntactic structure. Query languages, however, need to keep up with newly introduced annotation, allowing it to be searchable and easy to access.
We design an extension for the TIGER language which allows searching for frame structures along with syntactic annotation. To achieve this, the TIGER object model is expanded to include frame semantics, while remaining fully backwards-compatible.
Finally, these extensions have been added to our own implementation of TIGER, which includes novel indexing features not found in the original work of Lezius (2002a).
[Read more →]
Tags: coli · studies
It is possible to write custom GTK widgets that have “native” scrolling support, as opposed to just shoving them into a GtkViewPort and forgetting about them.
Apart from having mastered a small coding challenge, as it turned out to be, this also gives you greater control over the scrolling itself, like making sure that certain elements are visible, viewport panning etc.
Anyway, especially when using PyGTK, it’s a bit unclear on how to proceed. From the documentation, it somehow gets clear that it has to do with the signal set_scroll_adjustment_signal:
This signal is emitted when a widget of this class is added to a scrolling aware parent, gtk_widget_set_scroll_adjustments() handles the emission. Implementation of this signal is optional.
This is not a signal name, but a signal ID that has to be set in GtkWidgetClass.
Some more documentation reading reveals that you can set this signal by using the set_set_scroll_adjustments_signal method on a widget:
class ScrollableWidget(gtk.DrawingArea):
__gsignals__ = {
"set-scroll-adjustments": {
gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE, (gtk.Adjustment, gtk.Adjustment)),
}
def __init__(self):
gtk.DrawingArea.__init__(self)
self.set_set_scroll_adjustments_signal("set-scroll-adjustments")
It doesn’t really matter how you call the signal as long as it takes two arguments (the horizontal and vertical adjustment). This will make the method set_scroll_adjustments (which you can’t override from within Python) return True when it is called and signal that the widget supports scrolling.
This, however, is only half the way, because the scrollable widget still needs the adjustments handed in via said methods. It’s of course possible to connect to the signal explicitly, but there’s an even more direct way by using action signals.
Action signals are the C programmer’s idea of “generic methods”. In order to create such a signal, it has to have the flag gobject.SIGNAL_ACTION and they are directly connected to a function which is then called on each signal emission. While in C, you have to provide a function pointer, in Python you can just implement functions with a compounded magic name and have it called automatically. I haven’t found any documentation on that in the PyGObject or PyGTK docs, only some examples in the web:
class ScrollableWidget(gtk.DrawingArea):
__gsignals__ = {
"set-scroll-adjustments": {
gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_ACTION,
gobject.TYPE_NONE, (gtk.Adjustment, gtk.Adjustment)),
}
def __init__(self):
gtk.DrawingArea.__init__(self)
self.set_set_scroll_adjustments_signal("set-scroll-adjustments")
def do_set_scroll_adjustments(self, h_adjustment, v_adjustment):
# do some useful stuff here, like saving them
...
The method being called on emission has to start with do_, following by the signal names with hyphens replaced by underscores.
The adjustment objects can then be configured to one’s own liking to have scroll bars show up or not. However, to know when the user did some scrolling, it’s necessary to listen on some signals:
def do_set_scroll_adjustments(self, h_adjustment, v_adjustment):
h_adjustment.connect("value-changed", self._scroll_value_changed)
self._hadj = h_adjustment
v_adjustment.connect("value-changed", self._scroll_value_changed)
self._vadj = v_adjustment
To make the scroll bar show up, modify upper, lower and page_size on the adjustments.
self._hadj.lower = 0
self._hadj.upper = 50
self._hadj.page_size = 10
This tells the scrollbar that the size of the underlying picture (upper - lower) is 50, while the visible size (page_size) is 10.
The page size obviously depends on the current size of the widget, which can be retrieved from the underlying gtk.gdk.Window:
width, height = self.window.get_size()
The current position of the scroll bar is controlled by the property value of the adjustment object and should be in the range [lower .. upper - page_size]. Whenever the property is changed, the value-changed signal is emitted, which we’ve connected to previously, and the widget can be repainted.
If you’re curious, you can also see the whole gloriousness in actual working code.
Tags: lang:en · programming · python
Even before I started working at UZH, I got my new hardware. Back in February, when I visited Zürich to look for rooms, I was offered to choose a new notebook for myself. Since I already have a large notebook (at least that’s what I consider my 14.1″ T61) and toyed around with the idea of getting a desktop again (after 4 years of exclusive notebook use!), I chose an X301. I included some hardware upgrades that weren’t included in the basic offer:
- +2 GB RAM (4 GB overall)
- 3G card
- USB Port Replicator
- DisplayPort->DVI converter
Being in Switzerland, I had the choice between CH and US keyboard layout. Since CH is physically the same as the German layout (105 keys), I took it and haven’t really noticed that the keys have different symbols (as far as most of the special characters are concered) printed on that what appears on the screen when I hit them. I should really get Das Keyboard after all.
The X301 is not quite the workhorse the T61 is, which is especially noticeable from graphics speed, although that might be due to an Intel driver being in several different transitions right now. The SSD compensates for that, booting and starting up is a breeze. Fortunately, I don’t have to do much booting these days, because unlike other major graphics hardware creators, Intel’s developers are able to support powersave modes on Linux hardware, both Suspend-to-RAM and Suspend-to-disk.
Everything else works more or less, even the DisplayPort, dutyfully serving my 24″ screen at the university. The pièce de résistance is definitely the 3G card. I had to install it myself, which was quite a bit more involved than what I would have liked, but it worked on the second try. I got myself a prepaid data contract which allows me to surf the net for 3 CHF/h, which is a good complement to free wireless access at most public hotspots in Switzerland, which is due to SWITCH. The wireless became especially hand today, when I was shopping for a new router without having a clue about what’s good. Standing in the Media Markt (yes, this very pillar of the German retail market also metastasised into Switzerland) network hardware aisles browsing the web with a notebook probably didn’t look as superior as doing the same thing with a smartphone, but it helped me finding a router.
What really sets the X301 apart is the weight. I’m quite used to it by now, since I’ve had it for over a month, but it’s usually the first response I get when I hand it over to somebody else. It is, however, very noticeable in direct comparison, and the T61 feels clunky and hugely oversized when I carry it around or use it on my lap. The battery lifetime is okay, usually I get 3h under Linux. There’s probably some more running time to be gotten, but I’m more or less done tweaking the system for now and using it for actual work; or for blogging, emailing and chatting on my nice, new, comfy chair.
Tags: hardware · lang:en
… berühmte & tagesaktuelle politische Zitate in parallelen Korpora.
Tags: lang:de · shnasel
Wenn man sich die Mühe macht und auf einer Karte alle Stationen der letzten Jahre miteinander, kommt leider kein sonderlich spannendes Gebilde zusammen, im Sinne einer interessanten unterliegenden 2D-Struktur (Kreis, Pentagramm, Kassiopeia, Dependenzbaum) ist meine bisherige Umzugsaktivität also ein Misserfolg. Das einzige Muster ist eine leichte Alternation zwischen Nord- und Südumzügen (Süd-Nord-Süd-Nord-Süd), der nächste Umzug geht also nach höchstwahrscheinlich nach Murmansk.
Bis dahin also bin ich in Zürich, von der Urbanität her durchaus ein Aufstieg, dafür laufe ich aber deutlich länger als 5 min zum nächsten Karstadt (bzw. der Innenstadt). Das mir nächste Zentrum hat aber neben meinem Arbeitsplatz auch genügend andere Geschäfte zu bieten, sodass sich solche Verluste leichter verschmerzen lassen.
Nach einer dann doch wieder recht hektischen Umzugsphase bin ich seit dem 15. 4. offiziell Assistent an der Universität Zürich (immerhin Nummer 53 im Academic Ranking of World Universities und damit vor allen deutschen Universitäten). Das beinhaltet neben der Lehre (ab dem Herbstsemester) auch Arbeit am TreeAligner) und allem anderen, was mir so einfällt, bisher größtenteils Release-Polishing für die grandiose 1.1, nachdem es eine Version 1.0 nie gab—ein Release-Trick, den sich noch so einige von uns abschauen sollten. Sobald das abgeschlossen ist, dem Plan nach so Mitte Mai, geht es mit voller Kraft auf dem Query-Modul weiter, dazu aber morgen mehr.
Tags: lang:de · treealigner · uni · zürich
February 8th, 2009 · 2 Comments
Going through a computational linguistics program will bring you in touch with Zipf’s Law. Its core claim:
In a corpus, the frequency of any word is inversely proportional to its rank.
Translated into less-wordy terms, it means that some words (events) occur very often and many words only occur a few times, or only once.
Zipf’s Law also holds for similar structures like DNA, but the distribution can also be observed in the user reputation of Stack Overflow. The following three graphs contain the reputation (X) and the frequency of this particular reputation value (Y) on log-scaled axes. With increasing normalization, the plot gets more Zipf-like, with the typical long “tails” at the lower end.



If we plot the mass distribution of reputation orderd by decreasing reputation on log-log axes, we get something that looks like the cumulative of an exponential distribution:

On 2009-02-05, the total amount of reputation on Stack Overflow was 8,491,989, and around 15% of the users make up 85% of the reputation (not completely Pareto’s 80-20), with the top user (of 41,082) owning 0.39% of the overall reputation.
For these graphs, I’ve scraped the user overview pages, scraping every single user page would allow for more interesting (and accurate, since inactive users can be removed) statistics, but I’d rather wait for a proper API.
Tags: lang:en · other