Python unter Linux: wxPython

Aus Wikibooks

Sie haben nun einen weiteren Abschnitt erreicht, der sich mit grafischen Benutzeroberflächen in Python beschäftigt. Diesmal mit der plattformunabhängigen, auf wxWidgets aufbauenden Variante wxPython. Genauere Details zu den besonderen Aspekten wxPythons finden sich in den jeweiligen Abschnitten.


Vorbereitung und Installation[Bearbeiten]

Da wxPython nicht zum Standardrepertoire einer Pythoninstallation gehört, muss es erst installiert werden - und zwar überall da, wo die Anwendung später ausgeführt werden soll. Es gibt Installationspakete für alle möglichen Betriebssysteme. In den meisten Linux-Distributionen findet sich wxPython im Paketmanager. Falls das bei Ihnen nicht der Fall ist, können Sie Pythons setuptools oder ein Installationspaket verwenden. Die erste Variante sollte die einfachste sein, funktionierte bei mir im Test aber (ohne größere Schritte zur Aufklärung des Problemes) leider nicht:

Align=none Ausgabe

user@localhost:~$ sudo easy_install wxpython
Searching for wxpython
Reading http://pypi.python.org/simple/wxpython/
Reading http://wxPython.org/
Reading http://wxPython.org/download.php
Best match: wxPython src-2.8.9.1
Downloading http://downloads.sourceforge.net/wxpython/wxPython-src-2.8.9.1.tar.bz2
Processing wxPython-src-2.8.9.1.tar.bz2
error: Couldn't find a setup script in /tmp/easy_install-PkAhhW/wxPython-src-2.8.9.1.tar.bz2

Lassen Sie sich davon aber nicht beirren. Vermutlich brauchen Sie nur in Ihrem Paketmanager nach wxPython zu suchen und alles andere geht von selbst.

Plopp![Bearbeiten]

...oder welches Geräusch auch immer Sie mit dem Öffnen eines Fensters assoziieren: Mit wxPython erreichen Sie diesen Punkt jedenfalls einfach und schnell:

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

import wx

if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = wx.Frame(None, title="Hallo Welt!")
    frame.Show()
    app.MainLoop()

Kurz zur Erläuterung: In Zeile 4 importieren wir das Paket wx, welches alle Komponenten von wxPython beinhaltet. In älteren Beispielen könnten sich auch noch die Varianten "import wxPython as wx" oder "from wxPython import *" finden. Verwenden Sie diese nicht! Sie sind veraltet. Sternchen-Importe - wie in der zweiten Variante - sind außerdem generell nicht gern gesehen.

In Zeile 7 wird das Applikations-Objekt erstellt. Ohne dieses Objekt geht erstmal garnichts. Bei der Erstellung wird das grafische System, der Message- bzw. Eventhandler und alles weitere wichtige initialisiert. Die Nutzung von wx.PySimpleApp() macht das ganze am unkompliziertesten.

Zeile 8 erzeugt ein Frame-Objekt. Dieser stellt in unserem Beispiel das Hauptfenster dar. Eine Applikation kann aus mehreren Frames bestehen, aber nur einen Haupt-Frame haben. Allen weiteren muss dann als ersten Parameter den ihnen übergeordneten Frame übergeben werden. An dieser Stelle wird hier None übergeben, da ein Hauptframe keinen Parent hat. Bisher gibt es für diesen Parameter auch keinen Standard-Wert, so dass mindestens None angegeben werden muss.

Die folgende Zeile 9 sorgt dafür, dass der Frame auch angezeigt wird. Der Frame kann zwar auch dynamisch im laufenden Betrieb erzeugt und variiert werden, aber die initialen Elemente sollten erzeugt werden, bevor .Show() ausgeführt wird.

Abschließend starten wir die MainLoop, also die Haupt-Eventschleife von wxWidgets. Diese Schleife läuft so lange, bis das Hauptfenster (genauer alle Threads) in irgendeiner Weise geschlossen wird.

Bravo! Nun haben Sie das erste Fenster erstellt und auch schon einiges grundlegendes über wxPython gelernt. Das war auch gar nicht so schwer, oder?

Einen Schritt weiter[Bearbeiten]

Komplexere Anwendungen erben üblicherweise von Hauptelementen wie wx.Frame ab. Beispielsweise so:

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

import wx


class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Hallo Welt!")
        self.panel = wx.Panel(self)
        self.label = wx.StaticText(
            self.panel,
            label = "Python unter Linux",
            pos = (5, 5),
        )


if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show()
    app.MainLoop()

Je nach Anforderung wird beispielsweise auch von wx.Panel abgeerbt. Im Laufe dieses Tutorials werden wir ein entsprechendes Beispiel behandeln.

Windows[Bearbeiten]

In wx stammen alle sichtbaren Elemente von wx.Window ab. Somit wird jedes entsprechende Element von einfachem Text bis hin zur Menüleiste auch window genannt. Dabei unterscheiden wir zum einen zwischen Containerobjekten, die weitere Windows enthalten können, zum Beispiel wx.Frame, wx.Panel und zum anderen Controls: wx.StaticText, wx.TextCtrl, wx.Button. Diese Unterscheidung ist zwar etwas oberflächlich, aber im Moment völlig ausreichend für uns.

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

import wx


class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Hallo Welt!")
        self.panel = wx.Panel(self)
        self.label = wx.StaticText(
            self.panel,
            label = "Python unter Linux",
            pos = (5, 5),
        )


if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show()
    app.MainLoop()

Die Markierung zeigt den derzeitigen Hauptteil des Programmes. Falls Sie Zeile 9 nicht ganz verstehen, schauen Sie noch einmal zurück in das Kapitel OOP.

Das Hauptpanel des Frames wird in Zeile 10 angelegt und dann mit einem einfachen Text versehen. Die Parameter für wx.StaticText bekommen der Übersicht halber jeweils eine eigene Zeile und sollten selbsterklärend sein. Nähere Informationen finden Sie im entsprechenden Eintrag der API.

Hier sieht man gut den grundlegenden Aufbau eines Fensters. Das Panel dient als Eltern-Element fuer alle weiteren Controls. Es erscheint manchmal nicht notwendig, sollte aber immer genutzt werden. Manche Systeme können Controls nicht korrekt oder gar nicht darstellen, wenn sie direkt auf dem Frame angebracht werden.

Events[Bearbeiten]

Alle modernen grafischen Anwendungen arbeiten Eventbasiert. Ihre Elemente senden Signale an den sogenannten Eventhandler und empfangen sie von ihm. Der Löwenanteil der Funktionalität wird so realisiert. Hier werden wir nun darauf eingehen, wie man Events in wxPython benutzt.

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

import wx


class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, title="Hallo Welt!")
        self.panel = wx.Panel(self)
        self.label = wx.StaticText(
            self.panel,
            label = "Python unter Linux",
            pos = (5, 5),
            size = (self.Size.width - 10, -1),
        )

        self.input = wx.TextCtrl(
            self.panel,
            pos = (5, 30),
            size = (100, -1),
        )
         
        self.btn_text = wx.Button(
            self.panel,
            label = "Text ändern",
            pos = (110, 30),
            size = (95, -1),
        )
        self.btn_text.Bind(wx.EVT_BUTTON, self.on_change_text)

        self.btn_title = wx.Button(
            self.panel,
            label = "Titel ändern",
            pos = (210, 30),
            size = (95, -1),
        )
        self.btn_title.Bind(wx.EVT_BUTTON, self.on_change_title)


    def on_change_text(self, evt):
        self.label.Label = self.input.Value

    def on_change_title(self, evt):
        self.Title = self.input.Value


if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show()
    app.MainLoop()

Das Beispiel zeigt eine einfache Anwendung von Buttons. Mit Hilfe der Bind-Methode werden der Event-Binder wx.EVT_BUTTON und eine Methode an einen Knopf gebunden. Diese Methode wird dann aufgerufen, sobald der Knopf gedrueckt wird.

Alle Event-Binder fangen mit EVT_ an und sind komplett groß geschrieben. Im von mir genutzten wxPython 2.8.9.1 gibt es 233 solcher Objekte mit meist selbsterklärendem Namen. Einige davon werden wir hier im Laufe des Programmaufbaus noch kennenlernen.

Align=noneDetails

Wie funktioniert ein Event?
Objekte oder Methoden können jederzeit Events auslösen. Dabei erzeugen sie Instanzen
von entsprechenden Event-Klassen und schicken diese dann an den EventHandler.
Bei diesem sind auch die Bindungen der Events gespeichert, so das er die entsprechenden
Methoden mit der Instanz des ausgelösten Events aufrufen kann.

Anmerkungen[Bearbeiten]