Frage

Ich habe mit den Ruby-Bibliothek „Schuhe“ gespielt. Grundsätzlich können Sie eine GUI-Anwendung auf folgende Weise schreiben:

Shoes.app do
  t = para "Not clicked!"
  button "The Label" do
    alert "You clicked the button!" # when clicked, make an alert
    t.replace "Clicked!" # ..and replace the label's text
  end
end

Das machte mich denken - wie würde ich Design einen ähnlich schön zu bedienende GUI-Framework in Python? Eines, das die üblichen tyings von im Grunde seines Wrapper auf eine C * Bibliothek nicht existiert (Im Fall von GTK, Tk, wx, QT etc etc)

Schuhe nehmen die Dinge von Web devlopment (wie #f0c2f0 Artfarbe Notation, CSS-Layout-Techniken, wie :margin => 10) und von Ruby (ausführlich mit Blöcken in sinnvoller Weise)

Pythons Mangel an "rubyish Blöcken" macht ein (metaphorisch) -direct Hafen unmöglich:

def Shoeless(Shoes.app):
    self.t = para("Not clicked!")

    def on_click_func(self):
        alert("You clicked the button!")
        self.t.replace("clicked!")

    b = button("The label", click=self.on_click_func)

nicht annähernd so sauber, und wäre nicht fast als flexibel, und ich bin nicht einmal sicher, ob es implementierbar sein würde.

Dekorateure verwenden scheint wie eine interessante Art und Weise Codeblocks zu einer bestimmten Aktion zur Karte:

class BaseControl:
    def __init__(self):
        self.func = None

    def clicked(self, func):
        self.func = func

    def __call__(self):
        if self.func is not None:
            self.func()

class Button(BaseControl):
    pass

class Label(BaseControl):
    pass

# The actual applications code (that the end-user would write)
class MyApp:
    ok = Button()
    la = Label()

    @ok.clicked
    def clickeryHappened():
        print "OK Clicked!"

if __name__ == '__main__':
    a = MyApp()
    a.ok() # trigger the clicked action

Grundsätzlich ist der Dekorateur Funktion speichert die Funktion, dann, wenn die Aktion aufgetreten ist (sagen wir, ein Klick) die entsprechende Funktion ausgeführt werden würde.

Der Umfang der verschiedenen Sachen (sagen wir, das la Etikett in dem obigen Beispiel) könnte sein, ziemlich kompliziert, aber es scheint, in einer recht ordentlich Weise machbar ..

War es hilfreich?

Lösung

Sie können dies tatsächlich abziehen, aber es wäre mit metaclasses erfordern, die sind tief Magie (es sind Drachen). Wenn Sie ein Intro zu metaclasses wollen, gibt es eine Reihe von Artikel von IBM rel="nofollow , die die Ideen verwalten einzuführen, ohne Ihr Gehirn schmilzt.

Der Quellcode von einem ORM wie SQLObject könnte auch helfen, da es diese gleiche Art von deklarative Syntax verwendet.

Andere Tipps

## All you need is this class:

class MainWindow(Window):
    my_button = Button('Click Me')
    my_paragraph = Text('This is the text you wish to place')
    my_alert = AlertBox('What what what!!!')

    @my_button.clicked
    def my_button_clicked(self, button, event):
        self.my_paragraph.text.append('And now you clicked on it, the button that is.')

    @my_paragraph.text.changed
    def my_paragraph_text_changed(self, text, event):
        self.button.text = 'No more clicks!'

    @my_button.text.changed
    def my_button_text_changed(self, text, event):
        self.my_alert.show()


## The Style class is automatically gnerated by the framework
## but you can override it by defining it in the class:
##
##      class MainWindow(Window):
##          class Style:
##              my_blah = {'style-info': 'value'}
##
## or like you see below:

class Style:
    my_button = {
        'background-color': '#ccc',
        'font-size': '14px'}
    my_paragraph = {
        'background-color': '#fff',
        'color': '#000',
        'font-size': '14px',
        'border': '1px solid black',
        'border-radius': '3px'}

MainWindow.Style = Style

## The layout class is automatically generated
## by the framework but you can override it by defining it
## in the class, same as the Style class above, or by
## defining it like this:

class MainLayout(Layout):
    def __init__(self, style):
        # It takes the custom or automatically generated style class upon instantiation
        style.window.pack(HBox().pack(style.my_paragraph, style.my_button))

MainWindow.Layout = MainLayout

if __name__ == '__main__':
    run(App(main=MainWindow))

Es wäre relativ einfach in Python zu tun, mit einem bisschen, dass metaclass Python Magie wissen, wie. Die ich habe. Und die Kenntnis der PyGTK. Die ich auch. Ruft Ideen?

ich war nie auf metaclsses mit David Mertz Artikel bei IBM zufrieden so vor kurzem ich meine eigene metaclass Artikel . Genießen Sie.

Dieser extrem gekünstelt und nicht pythonic überhaupt, aber hier ist mein Versuch einer semi-wörtliche Übersetzung des neuen „mit“ Anweisung.

with Shoes():
  t = Para("Not clicked!")
  with Button("The Label"):
    Alert("You clicked the button!")
    t.replace("Clicked!")

Der schwierigste Teil mit der Tatsache zu tun, dass Python wird uns nicht gibt anonyme Funktionen mit mehr als einer Anweisung in ihnen. Um das zu umgehen, dass, könnten wir eine Liste von Befehlen erstellen und durch denjenigen laufen ...

Wie auch immer, hier ist der Back-End-Code, den ich lief dies mit:

context = None

class Nestable(object):
  def __init__(self,caption=None):
    self.caption = caption
    self.things = []

    global context
    if context:
      context.add(self)

  def __enter__(self):
    global context
    self.parent = context
    context = self

  def __exit__(self, type, value, traceback):
    global context
    context = self.parent

  def add(self,thing):
    self.things.append(thing)
    print "Adding a %s to %s" % (thing,self)

  def __str__(self):
    return "%s(%s)" % (self.__class__.__name__, self.caption)


class Shoes(Nestable):
  pass

class Button(Nestable):
  pass

class Alert(Nestable):
  pass

class Para(Nestable):
  def replace(self,caption):
    Command(self,"replace",caption)

class Command(Nestable):
  def __init__(self, target, command, caption):
    self.command = command
    self.target  = target
    Nestable.__init__(self,caption)

  def __str__(self):
    return "Command(%s text of %s with \"%s\")" % (self.command, self.target, self.caption)

  def execute(self):
    self.target.caption = self.caption

Mit etwas Metaclass Magie die Ordnung zu halten wir folgende Arbeits haben. Ich bin mir nicht sicher, wie pythonic es ist, aber es ist viel Spaß für die Erstellung von einfachen Dinge.

class w(Wndw):
  title='Hello World'
  class txt(Txt):  # either a new class
    text='Insert name here'
  lbl=Lbl(text='Hello') # or an instance
  class greet(Bbt):
    text='Greet'
    def click(self): #on_click method
      self.frame.lbl.text='Hello %s.'%self.frame.txt.text

app=w()

Der einzige Versuch, dies zu tun, die ich kenne Hans Nowak Wachs (die leider tot).

Die nächstgelegene Sie Blöcke rubyish bekommen kann, ist die mit Aussage von pep343:

http://www.python.org/dev/peps/pep- 0343 /

Wenn Sie PyGTK mit Lichtung und diese Lichtung Wrapper , dann PyGTK wird tatsächlich etwas pythonic. Ein wenig zumindest.

Grundsätzlich Sie das GUI-Layout in Glade erstellen. Sie können auch Ereignisrückrufe in Schneise angeben. Dann schreiben Sie eine Klasse für Ihr Fenster wie folgt aus:

class MyWindow(GladeWrapper):
    GladeWrapper.__init__(self, "my_glade_file.xml", "mainWindow")
    self.GtkWindow.show()

    def button_click_event (self, *args):
        self.button1.set_label("CLICKED")

Hier, ich gehe davon aus, dass ich einen GTK-Knopf irgendwo genannt button1 und ich angegeben button_click_event als geklickt Rückruf. Die Lichtung Wrapper nimmt viel Aufwand aus Ereigniszuordnung.

Wenn ich eine Pythonic GUI-Bibliothek zu entwerfen, würde ich etwas ähnliches, unterstützt die schnelle Entwicklung zu unterstützen. Der einzige Unterschied ist, dass ich würde sicherstellen, dass die Widgets zu einer mehr pythonic Schnittstelle. Die aktuellen PyGTK Klassen scheinen sehr C zu mir, außer, dass ich foo.bar verwenden (...) statt bar (foo, ...) obwohl ich genau bin nicht sicher, was ich anders machen würde. Wahrscheinlich lassen für einen Stil Django Modelle deklarative mittels Widgets und Ereignisse in Code hinterlegt und mit dem Sie Daten obwohl Iteratoren zuzugreifen (wo es Sinn macht, zum Beispiel Widget Listen vielleicht), aber ich habe nicht wirklich darüber nachdachte.

Vielleicht nicht so glatt wie die Ruby-Version, aber wie wäre es so etwas wie folgt aus:

from Boots import App, Para, Button, alert

def Shoeless(App):
    t = Para(text = 'Not Clicked')
    b = Button(label = 'The label')

    def on_b_clicked(self):
        alert('You clicked the button!')
        self.t.text = 'Clicked!'

Wie Justin sagte , zu implementieren diese müßten Sie eine benutzerdefinierte metaclass auf Klasse App, und eine Reihe von Eigenschaften auf Para und Button verwenden. Dies wäre eigentlich nicht zu hart sein.

Das Problem, das Sie in der nächsten laufen soll: Wie halten Sie den Überblick über die um , dass die Dinge in der Klassendefinition erscheinen? In Python 2.x gibt es keine Möglichkeit zu wissen, ob t oben b oder umgekehrt sein sollte, da Sie den Inhalt der Klassendefinition als Python dict erhalten.

Doch in Python 3.0 metaclasses werden in ein paar geändert von (minor) Wege. Einer von ihnen ist die __prepare__ Methode, die Sie Ihr eigenes Wörterbuch-ähnlichen Objekt zu liefern, ermöglicht es stattdessen verwendet werden - das bedeutet, dass Sie die Lage sein wird, in welcher Reihenfolge Elemente zu verfolgen sind definiert, und positioniert sie entsprechend im Fenster .

Dies könnte eine zu starke Vereinfachung sein, ich glaube nicht, dass es eine gute Idee, zu versuchen, eine Mehrzweck-ui-Bibliothek auf diese Weise zu machen. Auf der anderen Seite könnte man diesen Ansatz (metaclasses und Freunde) verwendet, um die Definition bestimmter Klassen von Benutzerschnittstellen für eine vorhandene UI-Bibliothek und in Abhängigkeit von der Anwendung zu vereinfachen, die tatsächlich Ihr eine erhebliche Menge an Zeit und Code-Zeilen speichern könnten.

Ich habe das gleiche Problem. Ich wan einen Wrapper um jeden GUI-Toolkit für Python zu erstellen, die einfach zu bedienen ist, und durch Schuhe inspiriert, aber brauchen ein OOP-Ansatz (gegen Rubin Blöcke) sein.

Weitere Informationen auf: http://wiki.alcidesfonseca.com/blog / python-universal-gui-revisited

Jeder ist willkommen, das Projekt beizutreten.

Wenn Sie wirklich UI codieren möchten, können Sie versuchen, etwas zu bekommen, ähnlich der django ORM; etw wie dies einen einfachen Hilfe-Browser zu bekommen:

class MyWindow(Window):
    class VBox:
        entry = Entry()
        bigtext = TextView()

        def on_entry_accepted(text):
            bigtext.value = eval(text).__doc__

Die Idee wäre, einige Container zu interpretieren (wie Fenster) als einfache Klassen, einige Container (wie Tabellen, v / HBoxen) von Objektnamen erkannt und einfache Widgets als Objekte.

Ich denke nicht, man muss alle Container innerhalb eines Fensters nennen würde, so dass einige Verknüpfungen wäre (wie im alten Stil Klassen als Widgets von Namen werden erkannt) wünschenswert.

über die Reihenfolge der Elemente: in MyWindow oben Sie müssen dies nicht verfolgen (Fenster ist vom Konzept her ein Ein-Slot-Container). In anderen Containern können Sie versuchen, den Überblick über die Ordnung zu halten unter der Annahme, dass jedes Widget-Konstruktor Zugriff auf einige globale Widget-Liste haben. Dies ist, wie es in django getan (AFAIK).

Ein paar Hacks hier gibt es einige Verbesserungen ... Es gibt noch einige Dinge zu denken, aber ich glaube, es ist möglich, ... und verwendbar, solange man komplizierte UIs nicht bauen.

Allerdings bin ich ziemlich glücklich mit PyGTK + Glade. UI ist nur eine Art von Daten für mich und es sollte als Daten behandelt werden. Es gibt einfach zu viel Parameter zu optimieren (wie Abstand an verschiedenen Orten) und es ist besser zu verwalten, dass ein GUI-Tool. Deshalb ich meine UI in Lichtung bauen, als XML speichern und mit gtk.glade.XML () analysiert werden.

deklarative ist nicht unbedingt mehr (oder weniger) pythonic als funktionelle IMHO. Ich denke, ein mehrschichtiger Ansatz wäre die beste (von unten nach oben):

  1. Eine native Schicht, und kehrt Python-Datentypen akzeptiert.
  2. Eine funktionelle dynamische Schicht.
  3. Ein oder mehr deklarativen / objektorientierte Schichten.

ähnlicher href="http://elixir.ematia.de/trac/wiki" rel="nofollow noreferrer"> Elixir + SQLAlchemy .

Persönlich würde ich versuchen, JQuery wie API in einem GUI-Framework zu implementieren.

class MyWindow(Window):
    contents = (
        para('Hello World!'),
        button('Click Me', id='ok'),
        para('Epilog'),
    )

    def __init__(self):
        self['#ok'].click(self.message)
        self['para'].hover(self.blend_in, self.blend_out)

    def message(self):
        print 'You clicked!'

    def blend_in(self, object):
        object.background = '#333333'

    def blend_out(self, object):
        object.background = 'WindowBackground'

Hier ist ein Ansatz, der geht über GUI ein wenig anders mit klassenbasierten Meta-Programmierung statt Vererbung Definitionen.

Dies ist largley Django / SQLAlchemy in inspiriert, dass sie stark auf Meta-Programmierung basiert und trennt Ihren GUI-Code von Ihrem „Code-Code“. Ich denke auch, es sollte die starke Nutzung von Layout-Managern machen wie Java, da tut, wenn Sie Code fallen, niemand ständig will Pixelausrichtung optimieren. Ich denke auch, es wäre cool, wenn wir CSS-ähnliche Eigenschaften haben könnte.

Hier ist ein grobes brainstormed Beispiel, das eine Säule mit einem Etikett nach oben, dann einem Textfeld, dann ein Knopf klicken auf dem Boden zeigen, die eine Meldung zeigt.

from happygui.controls import *

MAIN_WINDOW = Window(width="500px", height="350px",
    my_layout=ColumnLayout(padding="10px",
        my_label=Label(text="What's your name kiddo?", bold=True, align="center"),
        my_edit=EditBox(placeholder=""),
        my_btn=Button(text="CLICK ME!", on_click=Handler('module.file.btn_clicked')),
    ),
)
MAIN_WINDOW.show()

def btn_clicked(sender): # could easily be in a handlers.py file
    name = MAIN_WINDOW.my_layout.my_edit.text
    # same thing: name = sender.parent.my_edit.text
    # best practice, immune to structure change: MAIN_WINDOW.find('my_edit').text
    MessageBox("Your name is '%s'" % ()).show(modal=True)

Eine coole Sache zu bemerken ist die Art und Weise Sie die Eingabe von my_edit sagen MAIN_WINDOW.my_layout.my_edit.text verweisen können. In der Erklärung für das Fenster, ich glaube, es ist wichtig, willkürlich Kontrollen in dem Funktions kwargs der Lage sein, zu nennen.

Dies ist die gleiche App nur absolute Positionierung mit (die Kontrollen an verschiedenen Orten erscheinen, weil wir keine Lust Layout-Manager):

from happygui.controls import *

MAIN_WINDOW = Window(width="500px", height="350px",
    my_label=Label(text="What's your name kiddo?", bold=True, align="center", x="10px", y="10px", width="300px", height="100px"),
    my_edit=EditBox(placeholder="", x="10px", y="110px", width="300px", height="100px"),
    my_btn=Button(text="CLICK ME!", on_click=Handler('module.file.btn_clicked'), x="10px", y="210px", width="300px", height="100px"),
)
MAIN_WINDOW.show()

def btn_clicked(sender): # could easily be in a handlers.py file
    name = MAIN_WINDOW.my_edit.text
    # same thing: name = sender.parent.my_edit.text
    # best practice, immune to structure change: MAIN_WINDOW.find('my_edit').text
    MessageBox("Your name is '%s'" % ()).show(modal=True)

Ich bin mir nicht ganz sicher, aber wenn dies ein super großer Ansatz ist, aber ich denke auf jeden Fall, es ist auf dem richtigen Weg. Ich habe keine Zeit, um diese Idee mehr zu entdecken, aber wenn jemand dies als ein Projekt bis nimmt, würde ich sie liebe.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top