Herdentrieb

Mastodon - get hype

Warnung

Dieser Beitrag wird bedarfs- und kapazitätsabhängig aktualisiert. Da sich Mastodon in massiver, aktiver Entwicklung befindet, können Teile des Inhaltes veraltet sein.

Was bisher geschah

Mastodon ist ein freies, verteiltes soziales Netzwerk und gleichzeitig der Name dessen Server-Komponente. Ein Mastodon-Server innhalb des gesamten Netzwerkes (Fediverse) ist eine Instanz. Die Nutzung lehnt sich im Wesentlichen an Twitter an: ein Post (Toot, dt. Tröt) enthält bis zu 500 Zeichen, die wiederum favorisiert und erneut geteilt (Boost) werden können, es gibt das Follower-Prinzip und diverse Timelines (Nutzer, lokal, öffentlich).

Was passiert gerade?

Ein junges, Open-Source-Netzwerk zieht naturgemäß primär technikaffines Volk an, gefolgt von den üblichen Early Adopter-Kandidaten. Das führt derzeit in eine inhaltliche Mischung aus Tech-Themen, darunter viel, was sich um Mastodon selbst dreht, Rollenspiele, Manga, Furries, NSFW-Content und lahme SJW-Ansprachen.

Wer bis jetzt noch nicht abgeschreckt ist, fragt sich:

Welche Instanz ist die richtige für mich?

Da prinzipbedingt jeder einen Mastodon-Server betreiben kann, tun dies auch viele. So explodiert aktuell die Zahl der Instanzen. Aber es ist eben auch damit zu rechnen, dass die Mehrheit nur kurzfristig aus Experimentierfreude existieren wird. Verschwindet eine Instanz, verschwinden auch alle Accounts (und deren Inhalte) in dieser.

Ein weiterer Punkt ist die Ausrichtung der Instanz, sei sie themenbezogen (Spaß/Memes/Aktivismus/Tech) oder lokal/landessprachlich oder der Grad an Moderation.

Hier zeichnet sich gerade eine Art Sortierungsbewegung ab: Benutzer migrieren zu Instanzen, die eher ihren Interessen bzw. ihrem Umfeld entsprechen.

Bisher ist es nicht möglich, die lokale Timeline einer Instanz ohne Registrierung anzusehen und man hier nahezu die Katze im Sack kauft. Jüngst gibt es aber Abhilfe unter

Wenn man also nicht genau eine Instanz favorisiert, weil sie thematisch passt oder man dort schon Nutzer kennt, sucht man sich am besten eine größere, allgemeine Instanz aus, bei der eine größere Chance besteht, die erste Euphoriewelle zu überleben.

Wichtiger Hinweis

Accounts können zur Zeit nicht gelöscht werden. Auch können Accounts nicht in andere Instanzen migriert werden. Es ist zwar möglich, eine Liste der Accounts, denen man selbst folgt, zu exportieren und in neue Accounts zu importieren, dies funktioniert aber nicht für Follower.

Okay, ich bin dabei, was muss ich tun?

Hashtags

Tags sind die (aktuell) beste Art, bei Mastodon nach Stichwörtern zu suchen. Entweder sucht man direkt in der Suchleiste in der linken Spalte oder man ruft die URL <instance>/tags/<tag> auf. In den Suchergebnissen werden auch Beiträge anderer Instanzen angezeigt, dies erfolgt aber nicht kongruent, man vergleiche beispielsweise den Tag #bicycle auf

User-Feed

Für jeden Nutzer wird ein Atom-Feed bereitgestellt. Leider gibt es dies nicht für Tags.

Klingt irgendwie nicht nach dem großen Twitter-Killer

Korrekt, ist es möglicherweise auch nicht. Man merkt dem Projekt definitiv an, dass es noch in den Kinderschuhen steckt. Es ist recht aufwändig, sich nach Inhalten und Usern umzusehen.

Das erinnert mich ein wenig an den "Facebook-Killer" Google+, als soziales Netzwerk hat es seine Nische gefunden, aber gerade große Medien, Blogger und die egomane Twitter-"Elite" haben die Funktionsweise von G+ nie verstanden (und wollten das auch größtenteils nicht). Damit will ich die Versäumnisse und das akkurate und stete Verschlimmbessern der Plattform seitens Google in keiner Weise in Schutz nehmen.

BeeLine - erster Eindruck

Technikgeraffel

BeeLine

Das Gerät verfügt über einen internen Akku, der etwa 30 Stunden im Betrieb halten soll. Das E-Ink-Display ist energiesparend, wetterfest und sonnenlichtverträglich, für den Nachtbetrieb gibt es eine zuschaltbare Beleuchtung, die Bedienung erfolgt über die vier äußeren Touch-Tasten und die zugehörige App.

App

/images/BeeLine/app_main.thumbnail.png

App: Zielauswahl

Nach der BeeLine-Anmeldung und dem Pairen des Gerätes per Bluetooth geht es los. Unter "Where to?" tippt man auf der Karte auf das Ziel und drückt den gelben "BeeLine"-Button. Alternativ wählt man unter "Destinations" ein Ziel aus, das sich auch speichern und wieder aufrufen lässt. Ziele mit dazwischenliegenden Wegpunkten kann man als Route speichern.

Die App verfügt auch über die Kompass-Ansicht des BeeLine, könnte also theoretisch auch ohne Gerät mit einer Lenkerhaltung genau so genutzt werden.

/images/BeeLine/app_compass.thumbnail.png

App: Kompassansicht

Fahrrad

BeeLine lässt sich durch das flexible Gummiband an diversen Stellen am Fahrrad befestigen. Die Ausrichtung lässt sich in der App um 90° drehen, so dass sowohl der Lenker als auch der Vorbau oder das Oberrohr als Befestigungspunkte in Frage kommen.

/images/BeeLine/mount.thumbnail.jpg

Befestigung am Vorbau

Die Befestigung erweist sich als wesentlich flexibler, stabiler und vertrauenswürdiger als etwa die Bike Citizens-Halterung, die ich für ziemlich ungelungen halte.

Sind wir schon da?

BeeLine visiert nun das Ziel an, die angezeigte Entfernung entspricht der Luftlinie.

/images/BeeLine/app_map.thumbnail.jpg

App: aktueller Standort und Ziel

Da ich meist ohne E.T. unterwegs bin, gebietet es die Neugier, über unbekannte Wege zum Ziel abkürzen zu wollen. Das hat spontan bei mir nicht geklappt, ich musste wieder umkehren, aber immerhin weiß ich nun, dass es in Treptow das hier gibt:

/images/BeeLine/unicorn.thumbnail.jpg

Get hype

Blöderweise bin ich kein Einhornfan und finde den Hype darum inzwischen nur noch unkreativ bis peinlich (gilt nicht für Leute, die schon immer ein Herz für Einhörner hatten und nicht auf den fahrenden Zug aufgesprungen sind). Aber das Potential, beim Sichverfahren etwas Neues zu entdecken, ist definitiv gegeben.

Wird die Fahrt beendet, was nicht automatisch beim Erreichen des Zieles ist, wird diese in der App gespeichert.

/images/BeeLine/app_route.thumbnail.png

App: beendete Routen werden gespeichert

Verbesserungspotential

Wegpunkte

Man kann irgendwie zwischen Wegpunkten switchen, man sieht auf dem Display aber nicht, bei welchem man gerade ist und offenbar kann man nur vorwärts "durchblättern". Das muss ich nochmal testen.

Fahrt versehentlich beenden

Mir ist es jetzt zweimal passiert, dass die Fahrt versehentlich beendet wurde und die Strecke auch nicht hinterher in der App unter "Rides" aufgeführt war. Ich glaube, da steckt mehr als nur ein Bedienungsfehler hinter, das muss ich ebenfalls nochmal genauer untersuchen.

Bedienung

Das Gerät verfügt über vier Touch-Buttons (N/S/E/W), die man aber eher so durchklickt. Es ist nicht ersichtlich, welche Funktionen dahinter stecken.

Routen importieren

Die App speichert die Route, es wäre also auch möglich, Routen zu importieren. Genial fände ich einen alternativen Navigationsmodus, der nach Strecke navigiert, dann hätte man gleichzeitig einen optisch ansprechenden Garmin-Ersatz, eine eierlegende Navi-Sau sozusagen.

Pros and Cons

Pro Contra
einfache Befestigung Halterung birgt Gefahr der Materialermüdung, Ersatzhalterungen sollen zukünftig erhältlich sein
lange Akkulaufzeit funktioniert nicht ohne App
minimalistische Navigation minimalistische Navigation
optisch ansprechend Preis-Leistungs-Verhältnis (115 Euro)
Display gut lesbar  
lenkt beim Fahren nicht ab  
verleitet dazu, neue Wege zu testen  

Dateiauswahldialog

FileChooserDialog

Der GtkFileChooserDialog ist eine Subclass von GtkDialog (siehe Artikel zu Dialogen) und ermöglicht das Auswählen und Speichern von Dateien oder Ordnern.

/images/16_fcd.thumbnail.png

Glade

Den Dialog findet man in der Widget-Seitenleiste oben unter "Oberste Ebene". Neben dem Dateibrowser besitzt er eine erweiterbare interne GtkBox für weitere Widgets sowie eine GtkButtonBox als interne "action area" für Buttons.

Es ist erforderlich anzugeben, für welche Aktion der Dialog gedacht ist, was Gtk.FileChooserAction entspricht (siehe Python GI API Reference): Datei öffnen oder speichern, Ordner auswählen oder anlegen.

Action area und Responses

Responses sind Antwortkennungen, die beim Auslösen des Signals response übergeben werden. Buttons in der "action area" werden jeweils Response-Werte zugewiesen anstatt das clicked-Signal der Buttons zu nutzen.

Standardmäßig wird die "action area" unter dem Dateibrowserbereich angelegt.

/images/16_fcd_glade.thumbnail.png

Verwendet man den FileChooserDialog ohne Glade (siehe unten), werden die Buttons in der Headerbar angezeigt. Letzteres sollte aber vermutlich der Standard sein, da es eine Warnung ausgegeben wird, die die Funktionalität des Dialogs allerdings nicht beeinträchtigt:

Gtk-WARNING **: Content added to the action area of a dialog using header bars

Diese Meldung wird nicht angezeigt, wenn man darauf verzichtet, in Glade Buttons zur intern action area hinzuzufügen. Dies betrifft auch andere Dialogarten.

Legt man nun in Glade eine Headerbar mit Buttons an, ist es standardmäßig nicht möglich, diesen Buttons Response-Werte zuzuweisen.

Dafür gibt es (mindestens) zwei Lösungsmöglichkeiten:

XML-Datei

Man legt die Headerbar mit Button(s) an, anschließend öffnet man die Glade-Datei in einem Texteditor und fügt dem Element <action-widgets> die entsprechenden Zeilen hinzu:

<object class="GtkFileChooserDialog" id="filechooser_dialog">
  <property abc ></property>
  <property xyz ></property>
  <!-- usw. -->
  <action-widgets>
    <!-- Buttons innerhalb der action area -->
    <action-widget response="0">button1</action-widget>
    <action-widget response="1">button2</action-widget>
    <!-- Button in Headerbar -->
    <action-widget response="-1">hb_button</action-widget>
  </action-widgets>
  <!-- usw. -->
</object>

Dies funktioniert zwar, ist aber ganz sicher nicht so gedacht, weil diese Änderung beim erneuten Bearbeiten der Glade-Datei wieder rückgängig gemacht wird.

add_action_widget-Funktion

Mit der Funktion add_action_widget können aktivierbare Widgets zur action area hinzugefügt und damit ebenfalls per response-Signal verarbeitet werden. Dies sind Widgets der Gtk.Activatable-Klasse und beinhaltet die Widgets Buttons, MenuItem, RecentChooserMenu, Switch und ToolItem.

Ein Button wird nach dem Schema

widget.add_action_widget(button,response)

hinzugefügt. Wichtig ist es, beim Button die Widget-Eigenschaft "can-default" zu aktivieren:

button.set_property("can-default",True)

Im Beispiel erhält der Dialog die beiden Standardbuttons "Anwenden"/"Abbrechen":

button = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL)
button.set_property("can-default",True)
self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.CANCEL)
button = Gtk.Button.new_from_stock(Gtk.STOCK_APPLY)
button.set_property("can-default",True)
self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.OK)

Um die Dateiauswahl auch auf Doppelklick zu ermöglichen, wird neben des response-Signals noch das Signal file-activated benötigt.

Vorschau-Widget

Der Dialog besitzt die Option ein Vorschau-Widget einzubinden. Dafür aktiviert man in den Dialog-Eigenschaften "Vorschau-Widget aktiv" und wählt unter "Vorschau-Widget" ein freies Widget (z.B. ein GtkImage). Möglicherweise muss man dieses Widget zunächst in ein leeres Container-Widget erstellen und dort in einen freien Bereich ziehen.

Wenn eine Aktualisierung der Vorschau angefordert wird, wird das Signal update-preview ausgelöst.

FileFilter

FileFilter dienen dazu, Dateien bestimmten Musters anzuzeigen. Pro Filter können mehrere (shell style glob) Patterns oder MIME-Types angegeben werden.

Den Filter findet man in Glade unter "Sonstiges". Im Dialog kann man in den allgemeinen Widget-Einstellungen den gewünschten Filter auswählen. Dies entspricht der set_filter-Funktion.

Python

Dialog ohne Glade

Der FileChooserDialog lässt sich auch ziemlich einfach ohne Glade realisieren, zudem lassen sich die oben genannten Probleme mit Buttons in der Headerbar vermeiden. Der Dialog wird nach folgendem Schema erstellt:

dialog = Gtk.FileChooserDialog("window title",
                            parent_window,
                            file_chooser_action,
                            (button1,response1,
                            button2,response2))

Der Dialog wird dann direkt aufgerufen und verarbeitet:

response = dialog.run()
if response == response1:
    ...
elif response == response2:
    ...
dialog.destroy()

FileFilter

Es gibt zwei Möglichkeiten, einen Filefilter anzuwenden:

  1. Ohne Wahl. Der anzuwendende Filter ist voreingestellt:
dialog.set_filter(filter)
  1. Wahl per Dropdown-Menü: Der Nutzer kann zwischen mehreren vorgegebenen Filtern wählen:
dialog.add_filter(filter1)
dialog.add_filter(filter2)
...

Weiterlesen…

Desktopintegrationsbemühungen

Desktopintegration: Icon, Headerbar, Kommandozeilenoptionen

(Fortsetzung zum Artikel GtkApplication)

/images/15_application.thumbnail.png

Glade

Icon

Einem Fenster lässt sich direkt in Glade unter "Allgemein > Darstellung > Symboldatei" ein Icon auswählen. Das Problem dabei ist, dass Glade Bilddateien nur anzeigt, wenn sie sich im selben Verzeichnis wie die Gladedatei selbst befinden, auch wenn man ein anderes Verzeichnis auswählt.

Am einfachsten behebt man dies, indem man die Gladedatei in einem Texteditor bearbeitet und den (relativen) Pfad zum Icon angibt. Diese Einstellung bleibt auch erhalten, wenn die Datei später mit Glade bearbeitet und gespeichert wird:

<object class="GtkApplicationWindow" id="window">
  ...
  <!-- <property name="icon">duckyou.svg</property> -->
  <property name="icon">../files/duckyou.svg</property>
  ...

Headerbar

Die Headerbar wurde mit GNOME 3.10 eingeführt und vereint Titelleiste und Toolbar in einem Widget, d.h neben Titel und Untertitel können rechts und/oder links verschiedene Widgets (Menüs, Buttons) angelegt sowie clientseitige Fensterkontrollknöpfe angezeigt werden.

Die Headerbar ist optional. Möchte man sie nutzen, muss in den Fenstereinstellungen "Allgemein > Darstellung > Klienseitige Fensterdekoration" ausgewählt werden. Daraufhin erscheint im oberen Bereich des Fensters ein reservierter Bereich, in dem die Headerbar platziert wird. Wird die Headerbar außerhalb davon platziert, wird weiterhin zusätzlich die normale Titelleiste angezeigt.

/images/15_headerbarglade.png

Kommandozeilenoptionen

GtkApplication stellt die erforderlichen Mittel für anwendungseigene Kommandozeilenoptionen zur Verfügung (Handling command line options in GApplication).

Optionen anlegen

Verfügbare Optionen werden mit der Funktion add_main_option_entries(entrylist) hinzugefügt. Diese Einträge haben das Format GLib.OptionEntry, welches allerlei Parameter besitzt.

def __init__(self):
    self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0))
    self.app.add_main_option_entries([
        self.create_option_entry("--version", description="Show version numbers and exit"),
        self.create_option_entry("--setlabel", description="Set label widget",arg=GLib.OptionArg.STRING,),
        self.create_option_entry("--bollocks", description="Additional test option - exit"),
    ])

def create_option_entry(self,
                        long_name,
                        short_name=None,
                        flags=0,
                        arg=GLib.OptionArg.NONE,
                        arg_data=None,
                        description=None,
                        arg_description=None):
    option = GLib.OptionEntry()
    option.long_name = long_name.lstrip('-')
    option.short_name = 0 if not short_name else ord(short_name.lstrip('-'))
    option.flags = flags
    option.arg = arg
    option.arg_data = arg_data
    option.description = description
    option.arg_description = arg_description
    return option

Shortnames

Eine Option kann ein aus einem Buchstaben (oder besser gesagt "printable ASCII character different from ‘-‘") bestehenden Synonmym besitzen, den Shortname. Bei der Option --help ist dies gemeinhin -h.

Die short_name-Variable von OptionEntry ist allerdings integer. Die in der Dokumentation nicht ersichtliche Lösung besteht darin, in der Variable die Dezimalkodierung des entsprechenden Zeichens zu übergeben, also etwa 97 für "a". Bei ungültigen Werten wird eine Fehlermeldung ausgegeben. Optionen ohne Shortname erhalten den Wert 0.

Signal verbinden

Der GtkApplication-eigene "handle-local-options"-Handler verarbeitet die Optionen. Sobald Optionen angelegt sind, wird dieses Signal noch vor dem "startup"-Signal ausgelöst

self.app.connect("handle-local-options", self.on_local_option)

Optionen verarbeiten

Die an die Handler-Funktion übergebene option ist ein Element der Klasse GLib.VariantDict. Mit contains("option") lässt sich nach der übergebenen Option suchen.

def on_local_option(self, app, option):
    if option.contains("option1"):
        #do something and exit normally
        return 0
    elif option.contains("option2"):
        #do something different and exit
        return 0
    elif option.contains("option3"):
        #do more and continue
    return -1

Ein übergebener String kann extrahiert werden, indem GLib.VariantDict mit end() in GLib.Variant konvertiert wird, das sich wiederum mit keys() auslesen lässt:

var = GLib.VariantDict.end(option)
option_string = var[var.keys()[0]]
Ein Return-Wert ist zwingend erforderlich, er entspricht dabei dem Exit-Status:
  • -1: Anwendung wird weiter ausgeführt
  • 0: erfolgreiche Ausführung, Anwendung wird beendet, "startup/activate" werden nicht ausgeführt
  • 1 bzw. positiver Wert: nicht erfolgreiche Ausführung, Anwendung wird beendet

Optionen übergeben

Die Option, die immer verfügbar ist, ist --help. Hier werden unter "Anwendungsoptionen" die angelegten Optionen samt Beschreibung aufgeführt. Die Optionen können wie definiert angegeben werden:

$ python script.py --version
Python: 3.6.0
GTK+:   3.22.6

oder mit --setlabel einen String an Gtk.Label übergeben:

$ python script.py --setlabel "I can haz options!"

Weiterlesen…

Selbständig

Programm als GtkApplication laufen lassen

GtkApplication handhabt verschiedene wichtige Aspekte einer GTK+-Anwendung, wie etwa der GTK+-Initialisierung, dem Sessionmanagement und der Desktopintegration.

/images/14_application.thumbnail.png

XML-Dateien

Glade

In Glade verändert sich im Prinzip nichts. Als Hauptfenster sollten GtkApplicationWindows zum Einsatz kommen. Als Beispiel wird hier das Gladefile aus dem Artikel zu Dialogen wieder verwendet.

GMenu

Die GNOME-Shell unterstützt Appmenüs, erreichbar über das obere Panel. Die XML-Datei muss so formatiert sein, dass sie als GioMenu erkannt wird:

<?xml version="1.0"?>
<interface>
 <menu id="appmenu">
    <section>
      <item>
        <attribute name="label" translatable="yes">Menu Item</attribute>
        <attribute name="action">app.item</attribute>
      </item>
    </section>
  </menu>
</interface>

Von Glade selbst würde diese XML-Datei als veraltetes Format erkannt, aber sie lässt sich trotzdem von GtkBuilder laden und anschließend kann man die Identifier nutzen.

Python

Initialisierung von GtkApplication

Bei der Initialisierung wird eine application_id- und flags-Angabe benötigt. Letztere können in der Regel bei 0 bzw. FLAGS_NONE belassen werden (siehe Gio.ApplicationFlags), die Konventionen für die application_id sind hier dokumentiert.

Die Application kann nun mit verschiedenen Signalen verbunden werden, die zu bestimmten Ereignissen ausgelöst werden, aber es muss mindestens activate verbunden werden:

def __init__(self):

    self.app = Gtk.Application.new("org.application.test", 0)
    #self.app.connect("startup", self.on_app_startup) #optional
    self.app.connect("activate", self.on_app_activate)
    #self.app.connect("shutdown", self.on_app_shutdown) #optional

def on_app_activate(self, app):

    #setting up GtkBuilder etc.
    ...
    ...
    ...

Appmenu

Wie oben bereits erwähnt, lässt sich die GMenu-XML von GtkBuilder laden, dann wird das Menü der Application zugewiesen:

builder.add_from_file("menu.ui")
app.set_app_menu(builder.get_object("appmenu"))

Die zu den Menüeinträgen verknüpften Funktionen müssen nun als Actions, genauer GioSimpleActions, erstellt und analog zur herkömmlichen Signalverknüpfung über connect verbunden werden.

def add_simple_action(self, name, callback):
    action = Gio.SimpleAction.new(name)
    action.connect("activate", callback)
    self.app.add_action(action)

Im Beispiel werden Actions zum Aufrufen der Dialoge erstellt.

Starten und Beenden

GtkApplication übernimmt die Handhabung des GTK+-Mainloops, das heißt, es nicht mehr notwendig GTK+ manuell zu starten oder zu beenden. Stattdessen werden run() und quit() verwendet:

Gtk.main()      ->  app.run(argv)
Gtk.main_quit() ->  app.quit()

Beendet man das Programm über den [X]-Button oder den "Schließen"-Eintrag des Appmenus (immer vorhanden), wird automatisch das "shutdown"-Signal ausgelöst (siehe oben). Das heißt, es müssen keine entsprechenden Signale definiert werden. "Shutdown" wird auch ausgelöst, wenn bei der Initialisierung nicht mit einer Funktion verbunden wird.