Mittwoch, 18. Januar 2012

To GUI or not to GUI...

Schaut man in den Programmierforen nach, so kommt von Einsteiger öfters die Frage: "Ich möchte mein erstes Programm schreiben, das auch eine GUI haben soll.". Eine oft gehörte Antwort ist dann: "Programmier' erst Mal ohne GUI".

Das mag befremdlichen klingen, da seit vielen Jahren eine grafische Benutzeroberfläche absoluter Standard ist. Warum soll man also nun für das (erste) eigene Programm darauf verzichten? Diese Frage wird im folgenden ein wenig beleuchtet.

Dazu gibt es folgende kleine Aufgabe: Es soll ein Programm geschrieben werden, welches eine ganze Zahl als Eingabe nimmt und das Quadrat dieser Zahl als Ausgabe zurück liefert. Um das Programm einfach zu halten und den Fokus auf "To GUI or not to GUI" zu legen, soll das Programm lediglich prüfen, ob die Eingabe eine ganze Zahl ist. Wenn nicht soll eine Fehlermeldung ausgegeben werden. Weiterhin enthalten die Quelltexte keinerlei Kommentare.

Als Programmiersprache kommt Python zum Einsatz, wobei sich die Beispiele  sicherlich auch recht einfach auf andere Sprachen übertragen lassen.

Zurück zu "Programmier' erst Mal ohne GUI": Selbst Linux-Nutzer, welchen die Kommandozeile tendenziell geläufiger ist als z.B. Windows-Nutzer, erscheint es vielleicht doch ein wenig antiquiert "nur Text" zu Arbeiten. Der Grund für den Rat hierzu ist aber recht simpel: Selbst eine minimale GUI (wie im folgenden zu sehen) verlangt einiges mehr an Codezeilen. Plus, und das ist der wichtigere Punkt, die Einarbeitung in eine entsprechende Bibliothek mit entsprechend umfangreicher Dokumentation kann zeitaufwendig sein. Das heißt, dass der geneigte Programmieranfänger nicht nur die Grundlagen einer für ihn neuen Programmiersprache lernen soll, sondern auch direkt die Nutzung der GUI-Bibliothek - was die Sache definitiv nicht einfacher macht. Außerdem wäre es ja auch Schade, wenn er frustriert aufgibt, weil er (als Anfänger) nicht mit der GUI-Programmierung zurecht kommt.

Bevor wir zu den verschiedenen Versionen des Programms kommen noch eine Anmerkung vorab: Vor den Listings ist immer die Anzahl der Codezeilen angegeben. Die beinhalten nicht den Shebang und die Coding-Info (welche bei allen Listings so wie so gleich ist), weiterhin werden Leerzeilen nicht mit gezählt. Codezeilen, welche Zeilenumbrüche zur Verbesserung der Lesbarkeit enthalten, werden ebenfalls als eine Zeile gezählt.

Alle Programm sind unter Ubuntu 11.10 Oneiric getestet, die verwendete Python-Version bzw. GUI-Bibliotheken und Python-Anbindungen stammen alle aus den offiziellen Paketquellen.

Der erste Kandidat ist ein reines Kommandozeilen-Programm. Dieses erwartet, dass die zu quadrierende Zahl als Argument beim Programmaufruf mit übergeben wird. Heißt das Programm z.B. "quadrat.py", so lautet der Aufruf:

python quadrat.py 12

Hier das Programm für die Kommandozeile, 12 Zeilen:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

if len(sys.argv) < 2:
    print 'Es wurde kein Wert vorgegeben!'
    sys.exit()

eingabe = sys.argv[1]
try:
    zahl = int(eingabe)
except ValueError:
    print 'Der Wert ist keine ganze Zahl!'
    sys.exit()
else:
    print zahl * zahl


Update 21.1.2012:
Das obige Beispiel läuft zu ziemlich unter allen Python-Version, selbst unter sehr alten. Wer Python 2.7  bzw. 3.2 und neuer nutzt, der kann den Code noch kürzer gestalten, und zwar unter Verwendung des argparse-Moduls:

Kommandozeile mit argparse, 5 Zeilen:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import argparse

parser = argparse.ArgumentParser('Programm zum Quadrieren von Integerzahlen.')
parser.add_argument('value', type=int)
args = parser.parse_args()
print args.value * args.value


Danke an Hyperion für den Hinweis hierzu in deutschen Python-Forum!


Nun ist der Vergleich eines reinen Kommandozeilen-Programms vielleicht nicht ganz fair, weil dieses für jede Rechnung erneut aufgerufen werden muss. Die Programme mit grafischer Oberfläche müssen nur ein Mal gestartet werden, dann können beliebig viele Rechnungen durchgeführt werden.

Doch auch auf der Kommandozeile gibt es dafür ein pythonische Lösung, das cmd-Modul. Eine Einführung in dieses Modul ist im Blogeintrag cmd - Python-Modul für text-basierte Programme hier bei mir im Blog zu finden.

Die Umsetzung sieht dann so aus:

cmd-Modul, 14 Zeilen:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import cmd

class SimpleCmd(cmd.Cmd):
      
    def do_quadrat(self, line):
        try:
            zahl = int(line)
        except ValueError:
            print 'Der eingegebene Wert ist keine ganze Zahl!'
        else:
            print zahl*zahl

    def do_EOF(self, line):
            print 'Programm beenden...'
            return True

simplecmd=SimpleCmd()
simplecmd.cmdloop()

Kommen wir zu den GUI-Bibliotheken. Betrachtet werden hier die beiden wohl populärsten Frameworks, nämlich Qt4 und GTK+. Erster wird dabei über PySide genutzt, letzter über das PyGObject.
Pyside + Qt4, 27 Zeilen:

#!/usr/bin/env python
# ~*~ coding: utf-8 ~*~

import sys
from PySide import QtGui

class SimpleQt(QtGui.QDialog):
    def __init__(self,parent=None):
        super(SimpelQt, self).__init__(parent)
        self.setWindowTitle('Qt4')
        layout = QtGui.QVBoxLayout()
        self.eingabe = QtGui.QLineEdit()
        layout.addWidget(self.eingabe)
        self.button = QtGui.QPushButton(u'Quadrieren')
        layout.addWidget(self.button)
        self.label = QtGui.QLabel(u'Ergebnis...')
        layout.addWidget(self.label)
        self.setLayout(layout)
        self.button.clicked.connect(self.rechnen)

    def rechnen(self):
        wert = self.eingabe.text()
        try:
            zahl = int(wert)
        except ValueError:
            msgBox = QtGui.QMessageBox()
            msgBox.setText(u'Der eingegebene Wert ist keine ganze Zahl')
            msgBox.exec_()
        else:
            self.label.setText(unicode(zahl*zahl))

app=QtGui.QApplication(sys.argv)
simple_qt=SimpleQt()
simple_qt.show()
sys.exit(app.exec_())

Und hier noch die Version mit grafischer Oberfläche, welche auf GTK+ setzt:

PyGObject + GTK+, 29 Zeilen:

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
from gi.repository import Gtk
 
class SimpelGtk(object): 
    def Gtk.Window.__init__(self, title='Simpel Gtk+')
        self.window.set_default_size(200, 100)
        vbox = Gtk.Box(False, 20)
        vbox.orientation = Gtk.Orientation.VERTICAL
        self.window.add(vbox)
        self.eingabe = Gtk.Entry()
        vbox.pack_start(self.eingabe,True,True,0)
        button = Gtk.Button("Quadrieren")
        button.connect("clicked", self.rechnen)
        vbox.pack_start(self.button,True,True,0)    
        self.label = Gtk.Label("Ergebnis...")
        vbox.pack_start(self.label,True,True,0)
        
    def rechnen(self,data=None):
        try:
            wert = int(self.eingabe.get_text())
        except ValueError:
            text = 'Die Eingabe ist keine ganze Zahl!'
            dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR,
            Gtk.ButtonsType.CANCEL, 'Fehler bei der Eingabe')
            dialog.format_secondary_text(
                'Der eingegebene Wert ist keine ganze Zahl!')
            dialog.run()
        else:
            self.label.set_text(str(wert*wert))

    def main(self):
        win = Gtk.Window()
        win.connect("delete-event", Gtk.main_quit)
        win.show_all()
        Gtk.main()
 
if __name__ == "__main__":
    e = SimpelGtk()
    e.main()
    
#http://python-Gtk-3-tutorial.readthedocs.org/en/latest/introduction.html

Wie zu sehen ist, sind die GUI-basierten Varianten deutlich länger, nämlich rund 2,5x länger als die Kommandozeile-Version und circa 2x so lange wie die Version, welche das cmd-Modul nutzt. Das mehr an Codezeilen ist dabei natürlich durch das Hinzufügen der GUI-Elemente bedingt. Weiterhin ist der Quelltext für Anfänger (wahrscheinlich) deutlich schwieriger nach zu vollziehen.

Nur damit keine Missverständnisse entstehen: Dies ist kein Plädoyer gegen Programme mit GUI. Überhaupt nicht. Es soll lediglich gezeigt werden, wodurch der oft gehört und gut gemeinte Rat "Programmier' erst Mal ohne GUI" motiviert ist.

Abgesehen davon: beherrscht man die Grundlagen einer Progammiersprache ist es oft deutlich einfacher, die APIs weiterer Bibliotheken wie z.B. den zur GUI-Erstellung, nachzuvollziehen.

Kommentare:

  1. Vielleicht ist die passende in Zukunft, mach doch mal ne website, die ist auch bunt.

    AntwortenLöschen
  2. Gerade für Anfänger ist die GUI schlicht DIE Motivationsgrundlage zu programmieren und mit den passenden Tools ist das auch nicht weiter schwierig. Du propagierst im Grunde die Bottom-Up-Methode. Dein Argument logisch zu ende geführt, sollte der Anfänger ersteinmal Strukturamme zeichnen, bevor er sich mit der Komplexität einer bestimmten Programmiersprache befasst.

    Davon ab, dass wohl viele über HTML zu PHP oder Javascript kommen und damit eine GUI-Lose Programmierung ohnehin keinen Sinn mehr macht, da die GUI-API, also in diesem Fall das HTML, bereits die bekannte Komponente darstellt.

    Ansonsten gebe ich Dir Recht. Ich gestalte auch lieber ersteinmal die Funktionalität aus, bevor die GUI drankommt.

    AntwortenLöschen
  3. Hallo,

    das ist tatsächlich ein vieldiskutiertes Thema. Ich wollte nur ergänzen, dass es noch weitere Aspekte gibt, die man bei frühzeitigem GUI-Einstieg berücksichtigen sollte. Neben dem bloßen Umfang des Programmes, nimmt auch die Komplexität nicht unerheblich zu. Statt sich zunächst mit den vielen Sprach-Features in Python einzeln zu beschäftigen (Listen, Dicts, List Comprehensions, Iteratoren, Generatoren, Module, Klassen, Packages uvm), springt der Anfänger im Fall der GUI Programmierung gleich ins kalte Wasser und setzt sich mit einem recht großen Framework auseinander, bevor er überhaupt Python richtig verstanden hat. GUIs sind eine ganz eigene Welt - ebenso gut könnte man versuchen, als erstes GStreamer zu erlernen. Das hat mit Python nix zu tun und lenkt von den Eigenheiten der Sprache nur ab. Statt kanonisches Python zu lernen, frickelt der Anfänger sich eine Lösung zurecht, die irgendwie zum gewünschten Resultat führt - hier also etwa zu einem Fenster auf dem Bildschirm.

    Hinzu kommt, dass man GUIs, gerade wenn sie etwas größer werden, in Klassen einbetten möchte. Auch davon hat ein Anfänger in aller Regel noch nichts gehört und schustert sich aus irgendwelchen Beispielen lauffähige Programme zusammen, ohne genau zu verstehen, warum er das tut. Spätestens hier schlagen die vermeintlichen Lernerfolge ins Gegenteil um, weil der Anfänger sich von Anfang an einen falschen Umgang mit Klassen aneignet.

    GUIs sind darüber hinaus auch schlichtweg problematisch für Einsteiger - etwa wegen der Probleme mit der Nebenläufigkeit, auf die man früher oder später einmal stößt. Wenn dann auch noch angefangen wird, irgendwie mit Threads und Locks um diese Probleme herum zu arbeiten ("ich möchte einen Downloader mit Fortschritts-Anzeige"), hat man vollends schlechten Code, der schwer nachzuvollziehende Fehler produziert, die vorhanden Sprachmittel nicht ausschöpft und kaum noch zu lesen ist.

    GUIs kann man am besten als eigene kleine "Programmiersprachen" verstehen. Sie haben ihr eigenes Verhalten, ihre eigenen Probleme, haben bestimmte Design-Prinzipien, die man sich mal angesehen haben sollte (Interface Guidelines) und haben vor allem mit einer einzelnen Programmiersprache überhaupt nichts zu tun - sie lassen sich zwar anbinden, gehören aber nicht dazu. Eigentlich trifft es der GStreamer-Vergleich ganz gut. Niemand würde ernsthaft darüber diskutieren, ob man eine Programmiersprache besser lernt, indem man ein Audio-Framework anbindet. Warum machen wir das mit GUIs?

    AntwortenLöschen
  4. Danke für den Beitrag. Ich habe vor einem Jahr als absoluter Nicht-Programmierer genau diese Frage in einem Forum gestellt. Ich war davon ausgegangen, das Programmieren heute generell mit dem Gestalten der zugehörigen Fester usw einhergeht, was sich aber nicht bewahrheitet hat. Für mich war der Hinweis, es ohne GUI zu versuchen genau richtig. Die kleinen Jobs, die ich mittlerweile für den Hausgebrauch programmiert habe, lassen sich im Textmodus deutlich übersichtlicher gestalten. Ich habe mit Python in wirklich übersichtlichen kleinen Scripten sehr schnell Aufgaben erledigt, die die Erstellung einer GUI nicht gerechtfertigt hätten - und das hat schon viel Zeit in Anspruch genommen. Ich hab mal etwas über die Erzeugung von GUIs gelesen, aber es größtenteils nicht verstanden. Wenn man bereit andere Programmiersprachen kennt und Erfahrungen mit GUIs hat mag es natürlich anders aussehen. Insgesamt bin ich allerdings eher ein Fan von GUI Programmen - als Normalo-User sozusagen.

    AntwortenLöschen