Tag Archives: Code

Geliebte Einzeiler

Da mich das lautstarke Knarzen meiner Logitech MX518 beim Scrollen schon lange störte und auch die Oberfläche dieser nicht mehr wirklich angenehm war, gönnte ich mir nach langer Zeit einmal etwas Neues. Nach kurzer Suche fiel meine Wahl auf die Logitech M500:

Damit kann ich nun endlich laut- und scheinbar endlos scrollen. Die Kippfunktion des Mausrads ist allerdings nicht ganz glücklich geraten; bei einem Mittelklick löst man viel zu leicht die Vor/Zurück-Funktionalität aus. Das fehlen der Tasten zum Umstellen der Auflösung im Vergleich zur MX518 kann ich verschmerzen, da ich diese sowieso nie wirklich genutzt habe. Nichtsdestotrotz wünschte ich mir bei meiner neuen Maus eine höhere Auflösung; die augenscheinlich standardmäßigen 400 CPI sind mir zu wenig.

Von früheren Versuchen war mir noch das Werkzeug Lomoco bekannt, welches ebenso die Möglichkeit bietet, die Auflösung sowie einige anderen Parameter von Logitech-Mäusen anzupassen. Der Versuch sollte allerdings erst einmal fehlschlagen:

$ lomoco --1200
002.018: 046d:c069 Unsupported Logitech device: Unknown

Da hieß es nicht verzagen sondern Quellen laden. Die schließlich notwendige Anpassung erwies sich als einer der heißgeliebten Einzeiler:

--- src/lomoco.c    2011-03-05 22:06:12.000000000 +0100
+++ src/lomoco.c    2011-03-05 22:07:23.000000000 +0100
@@ -47,6 +47,7 @@
 {0xc025, "MX500 Optical Mouse",                        "M-BP81A",     0, 1, 1, 1, 0},
 {0xc031, "iFeel Mouse (silver)",                       "M-UT58A",     0, 1, 0, 0, 0},
 {0xc041, "G5 Laser Gaming Mouse",                      "M-UAC113",    0, 1, 0, 1, 0},
+    {0xc069, "M500 Laser Mouse",                           "M-500",       0, 1, 1, 0, 0},
 {0xc501, "Mouse Receiver",                             "C-BA4-MSE",   1, 0, 0, 0, 0},
 {0xc502, "Dual Receiver",                              "C-UA3-DUAL",  1, 0, 0, 0, 1},
 {0xc503, "Receiver for MX900 Receiver",                "C-UJ16A",     1, 0, 0, 1, 0},

Der Quellcode selbst dokumentierte in einfacher Form, wie man an die nötigen Angaben gelangen kann. (In diesem Fall der Inhalt von /proc/bus/input/devices)

Und damit kann ich nun meine neue Maus per Lomoco konfigurieren. Um das ganze festzuhalten erstellte ich auch gleich einen Report für das Lomoco-Debian-Paket.

Aktueller Befehl im Terminaltitel

Ein schönes Feature worauf ich nicht mehr verzichten möchte ist die automatische Anzeige des aktuell ausgeführten Befehls im Titel des Terminals bzw. des Tabs. Dadurch wird die Arbeit mit multiplen Fenstern bzw. Tabs ungemein einfacher. Hier die Umsetzung, einzutragen in die ~/.bashrc:

function reset_prompt_and_title() {
  # Gewünschten Standard-Prompt hier angeben
  PS1='\[\033[0;32m\]\u\[\033[00m\] \[\033[0;33m\]\w\[\033[00m\] \$ '
 
  echo -ne "\e]0;${USER}@${HOSTNAME}: ${PWD}\a"
  # Variante: Die überflüssige Angabe des Home-Verzeichnisses
  #           durch die übliche Tilde ersetzen
  #echo -ne "\e]0;${USER}@${HOSTNAME}: ${PWD/#${HOME}/~}\a"
}
 
# Nur für grafische Terminals gedacht
case "$TERM" in
  xterm*|rxvt*)
    # Eventuelle weitere Kommandos für $PROMPT_COMMAND unbedingt erst
    # nach dieser Zeile anhängen
    PROMPT_COMMAND=reset_prompt_and_title
 
    # Aktuell ausgeführten Befehl in den Terminaltitel schreiben
    trap 'echo -ne "\e]0;$BASH_COMMAND\a"' DEBUG
    ;;
esac

Zugriff auf USB-Datenträger mittels udisks

Um endlich in den Genuss eines aktuellen Thunars mit Gio-Unterstützung zu kommen begab ich mich auf eine kurze Suche und wurde im Xubuntu Developers PPA fündig. Die Aktualisierung verlief reibungslos, womit ich nun auch einmal auf entfernte Quellen zuzugreifen. So habe ich bisher auf mein Heimverzeichnis meiner Hochschule per sshfs und einem Eintrag in /etc/fstab zugegriffen. Grafisch einigermaßen komfortabel habe ich das Einhängen mit Hilfe einer benutzerdefinierten Aktion in Thunar vorgenommen.

Doch mittels Gio-Unterstützung in Thunar sollte dies der Vergangenheit angehören. Also rief ich ssh://ilux150.informatik.htw-dresden.de auf und erfreute mich der Tatsache, dass dies einfach funktionierte. Auch FTP-Zugriff funktioniert einwandfrei, auch wenn das wünschenswerte FTPES noch nicht in Gvfs implementiert wurde.

Schnell zeigte sich allerdings ein Problem: beim versuchten Zugriff auf meinen USB-Stick wurde ich nun mit einer Not Authorized Fehlermeldung abgewiesen. Nach kurzer Suche zu diesem Problem erkannte ich schnell, dass hier PolicyKit seine Finger im Spiel hat. Weiteres Suchen offenbarte mir schließlich eine Lösung, welche ich unter /etc/polkit-1/localauthority/50-local.d/plugdev.plka platzierte:

[Access to removable media for the plugdev group]
Identity=unix-group:plugdev
Action=org.freedesktop.udisks.drive-eject;org.freedesktop.udisks.filesystem-mount
ResultAny=yes

Damit allein war es jedoch noch nicht getan, da mir ck-list-sessions nach wie vor bescheinigte, dass die ConsoleKit-Sitzung nicht aktiv sei. Ohne eine aktive ConsoleKit-Sitzung könnte kein Programm unter X Dienste wie Udisks in Anspruch nehmen. Lange Rede kurzer Sinn war die Lösung schließlich, meine .xinitrc folgendermaßen zu gestalten:

exec ck-launch-session startxfce4

Hierdurch wird Xfce in einer ConsoleKit-Sitzung gestartet, womit ich nun ohne weiteres auf meine USB-Datenträger zugreifen kann. (Nachtrag: mit dem 4.7.0 Release von xfce4-session sollte dies nicht mehr erforderlich sein.) Meiner Meinung nach sollten Distributionen den hier beschriebenen Zugriff auf USB-Datenträger von Haus aus für reguläre Nutzer erlauben. Einige wie Ubuntu werden das auch sicher machen, bei Debian ist die Akzeptanz angesichts des serverlastigen Einsatzgebietes allerdings ungewiss.

Mausklicks in GtkLabel

Entgegen der landläufigen Behauptung ist es durchaus möglich, Mausklicks auf GtkLabel-Instanzen zu verarbeiten. Der Schlüssel liegt darin, GTK anzuweisen, dass das jeweilige Label ein GdkWindow haben soll. Aufmerksam gemacht wurde ich hierauf durch diesen Beitrag von Tadej Borovšak. Hier ein Beispiel:

#! /usr/bin/env python
 
import gtk
 
window = gtk.Window()
window.set_title('Label Button Press Test')
window.set_size_request(400, 300)
window.connect('destroy', lambda w: gtk.main_quit())
 
box = gtk.VBox(spacing=6)
window.add(box)
 
class ClickableLabel(gtk.Label):
    __gsignals__ = {'button-press-event': 'override'}
 
    def __init__(self, *args):
        gtk.Label.__init__(self, *args)
        # This is the important line to enable more signals
        self.set_has_window(True)
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
 
    def do_button_press_event(self, event):
        print 'Clickable: %s' % event.type
 
clickable_label = ClickableLabel()
clickable_label.set_markup('<span size="25000">Clickable label</span>')
box.pack_start(clickable_label, False)
 
def on_button_press_event(widget, event):
    print 'Regular: %s' % event.type
 
label = gtk.Label()
label.set_markup('<span size="25000">Regular label</span>')
# Does not work and renders the label invisible
label.set_has_window(True)
label.add_events(gtk.gdk.BUTTON_PRESS_MASK)
label.connect('button-press-event', on_button_press_event)
box.pack_start(label, False)
 
window.show_all()
gtk.main()

Wie zu sehen ist, lassen sich unter Nutzung einer einzelnen Zeile weitere Signale für ein GtkLabel „freischalten“. Jedoch nur für abgeleitete Typen, nicht für die Standard-GtkLabels, wie am zweiten Label zu erkennen ist. Warum dies so ist ist mir trotz Blick auf gtkwidget.c noch nicht ersichtlich. Erklärungen zur Methode set_has_window() erwähnen, dass diese nur in der Initialisierung eines Widgets aufgerufen werden sollte. Intern wird hier nur das GTK_NO_WINDOW-Flag gesetzt oder gelöscht und infolge dessen unter anderem das Zeichnen von Widgets anders gehandhabt. Aus diesem Grund hat ein GtkLabel bspw. auch keinen Hintergrund welchen man ändern könnte.

Iteration durch gtk.TreeModel

Zur Verarbeitung von Einträgen in einem gtk.TreeModel bedient man sich in Python üblicherweise der Iteration. Hierbei bietet gtk.TreeModel einige Methoden zur Arbeit damit. Ein übliches Konstrukt sieht damit so aus:

iter = model.get_iter_first()
while True:
    value = model.get_value(iter, 0)
    # Do something with value
    iter = model.iter_next(iter)
    if iter is None:
        break

Ziemlich umständlich und daher oft auch in dieser etwas vereinfachten Version vorzufinden:

iter = model.get_iter_first()
while iter:
    value = model.get_value(iter, 0)
    # Do something with value
    iter = model.iter_next(iter)

Doch wie so oft in Python gibt es eine viel intuitivere und simplere Lösung; gtk.TreeModel implementiert __iter__ und __next__, womit sich das obige Konstrukt auf folgenden Zweizeiler reduzieren lässt.

for row in model:
    value = row[0]

Bei row handelt es sich um eine gtk.TreeModelRow, welche intuitiven Zugriff auf die Daten der jeweiligen Spalten ermöglicht.

Operatoren-Verkettung in Python

Eher durch Zufall bin ich heute auf etwas gestoßen, was ich mir seit jeher in einer vernünftigen Programmiersprache gewünscht aber bis jetzt nie vorgefunden habe:

if a < b < c
    # …

Ein völlig intuitives und sehr gut lesbares Konstrukt zum einfachen Vergleich dreier oder mehr Operanden. In praktisch jeder Sprache muss man sich jedoch mit diesem Konstrukt behelfen:

if a < b and b < c
    # …

Nicht so jedoch in Python. Dort ist genau obiges möglich. Hierbei können offenbar beliebig viele Operanden verkettet werden. Eine feine Sache.

News LIST- und SINGLE-Ansicht auf einer Seite

In der Dokumentation zu tt_news wird ein Weg beschrieben, um die LIST- und SINGLE-Ansicht für News-Einträge in einer einzigen Plugininstanz nutzen zu können.

Unglücklicherweise ist dieses Vorgehen auf das althergebrachte Templating-System zugeschnitten und damit z. B. für TemplaVoilà nicht anwendbar.

Es gibt jedoch eine weitere Möglichkeit, die praktisch mit jedem Templating-System funktionieren sollte und nur wenige Zeilen TypoScript im Template der jeweiligen Seite erfordert:

plugin.tt_news {
 
  // Unset reference to Flexform field
  code >
  // Set default view, e. g. LIST
  code = LIST
}
 
[globalVar= GP:tx_ttnews|tt_news>0]
// Change code to SINGLE if there's a news record to display
plugin.tt_news.code = SINGLE
[global]

Damit dies jedoch funktioniert, darf in der Konfiguration für das News-Plugin nichts bei den Anzeigetypen (SINGLE, LIST, LATEST, …) ausgewählt werden. Andernfalls wird jegliche Einstellung im TypoScript ignoriert. Die Meldung, dass das Plugin hierdurch nicht konfiguriert ist, kann man getrost ignorieren.

Hinweis für die Version 3.0.0 der News-Erweiterung: das Formular für die Pluginkonfiguration wurde hier drastisch umgestaltet, wodurch es nunmehr unmöglich ist, keinen Wert für den Anzeigetyp festzulegen. Eine mögliche Lösung hierfür ist eine Modifikation des Flexforms der News-Erweiterung, was ich in diesem Zuge auch im Bugtracker von TYPO3 vorgeschlagen habe.

Das Resultat des ganzen Vorhabens sind kürzere Adressen durch die Einspaarung einer Seite.

E-Mail-Schutz vor Spambots

Einer Bitte folgend veröffentliche ich hier den – zugegeben nicht sonderlich komplexen – Code in meinem Impressum, welcher die E-Mail-Adresse vor Spambots verbirgt, bis der erforderliche Button betätigt wurde:

<?php
 
$str_value = 'E-Mail (Anzeigen)';
 
if (isset($_POST['showmail']) and $_POST['showmail'] == $str_value) {
 
    echo '      <p><a href="mailto:[email protected]">E-Mail-Adresse</a><p>' . "\n";
 
} else {
 
    echo '      <form action="" method="post" accept-charset="utf-8">' . "\n" .
         '        <p><input type="submit" name="showmail" value="' . $str_value . '" />' . "\n" .
         '      </form>' . "\n";
}
 
?>

Früher oder später wird auch der Code meines Gästebuches folgen. Ich hoffe früher.