gui framework descargar python user-interface frameworks

python - framework - ¿Cómo diseñarías un marco de interfaz de usuario muy "pitónico"?



python mobile framework (15)

He estado jugando con los "zapatos" de la biblioteca de Ruby. Básicamente puede escribir una aplicación GUI de la siguiente manera:

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

Esto me hizo pensar: ¿cómo diseñaría un marco de GUI igualmente agradable de usar en Python? Uno que no tiene los tics habituales de ser básicamente envoltorios de una biblioteca C * (en el caso de GTK, Tk, wx, QT, etc.)

Shoes toma cosas del desarrollo web (como la notación de color del estilo #f0c2f0 , técnicas de diseño CSS, como :margin => 10 ), y de ruby ​​(usa bloques de manera sensata)

La falta de Python de "bloques de rubíes" hace imposible (metafóricamente) un puerto directo:

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)

No está tan limpio, y no sería tan flexible, y ni siquiera estoy seguro de si sería implementable.

Usar decoradores parece una forma interesante de asignar bloques de código a una acción específica:

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

Básicamente, la función de decorador almacena la función, luego cuando la acción ocurrió (por ejemplo, un clic) se ejecutará la función apropiada.

El alcance de varias cosas (por ejemplo, la etiqueta en el ejemplo anterior) podría ser bastante complicado, pero parece factible de una manera bastante ordenada.


En realidad podrías lograr esto, pero requeriría el uso de metaclases, que son magia profunda (hay dragones). Si desea una introducción a las metaclases, hay una serie de artículos de IBM que logran introducir las ideas sin derretir su cerebro.

El código fuente de un ORM como SQLObject también podría ser útil, ya que usa este mismo tipo de sintaxis declarativa.


Con algo de magia Metaclass para mantener el orden, tengo el siguiente funcionamiento. No estoy seguro de cuán pitónico es, pero es divertido crear cosas simples.

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()


Declarativo no es necesariamente más (o menos) pythonic que funcional en mi humilde opinión. Creo que un enfoque en capas sería el mejor (desde arriba hacia arriba):

  1. Una capa nativa que acepta y devuelve tipos de datos de Python.
  2. Una capa dinámica funcional.
  3. Una o más capas declarativas / orientadas a objetos.

Similar a Elixir + SQLAlchemy .


El único intento de hacer esto que yo sé es la cera de Hans Nowak (que desafortunadamente está muerta).



Nunca estuve satisfecho con los artículos de David Mertz en IBM sobre metaclsses, así que recientemente escribí mi propio artículo de metaclass . Disfrutar.


Personalmente, intentaría implementar JQuery como API en un marco de GUI.

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''


Si realmente quieres codificar la interfaz de usuario, puedes intentar obtener algo similar al ORM de django; algo así para obtener un navegador de ayuda simple:

class MyWindow(Window): class VBox: entry = Entry() bigtext = TextView() def on_entry_accepted(text): bigtext.value = eval(text).__doc__

La idea sería interpretar algunos contenedores (como Windows) como clases simples, algunos contenedores (como tablas, v / hboxes) reconocidos por nombres de objetos y widgets simples como objetos.

No creo que haya que nombrar todos los contenedores dentro de una ventana, por lo que algunos accesos directos (como las clases antiguas se reconocen como widgets por nombres) serían deseables.

Acerca del orden de los elementos: en MyWindow anterior, no es necesario hacer un seguimiento de esto (la ventana es conceptualmente un contenedor de una ranura). En otros contenedores puede intentar hacer un seguimiento del orden suponiendo que cada constructor de widgets tenga acceso a alguna lista de widgets global. Así es como se hace en django (AFAIK).

Pocos trucos aquí, pocos retoques allí ... Todavía hay pocas cosas en las que pensar, pero creo que es posible ... y utilizable, siempre y cuando no construyas interfaces de usuario complicadas.

Sin embargo, estoy muy contento con PyGTK + Glade. La IU es solo un tipo de datos para mí y debe tratarse como datos. Hay demasiados parámetros para ajustar (como el espaciado en diferentes lugares) y es mejor administrarlo con una herramienta GUI. Por lo tanto, construyo mi UI en glade, lo guardo como xml y lo analizo usando gtk.glade.XML ().


Yo tengo este mismo problema. Quiero crear un envoltorio alrededor de cualquier kit de herramientas GUI para Python que sea fácil de usar e inspirado por Shoes, pero que debe ser un enfoque OOP (contra bloques de ruby).

Más información en: http://wiki.alcidesfonseca.com/blog/python-universal-gui-revisited

Cualquiera es bienvenido para unirse al proyecto.


Aquí hay un enfoque que trata sobre las definiciones de la GUI de una manera un tanto diferente usando la meta-programación basada en la clase en lugar de la herencia.

Este es un gran Django / SQLAlchemy inspirado en que se basa en gran medida en la meta-programación y separa el código de su GUI de su "código de código". También creo que debería hacer un uso intensivo de los administradores de diseño, como lo hace Java, porque cuando se elimina el código, nadie quiere ajustar constantemente la alineación de píxeles. También creo que sería genial si pudiéramos tener propiedades tipo CSS.

Aquí hay un ejemplo de tormenta de ideas que mostrará una columna con una etiqueta en la parte superior, luego un cuadro de texto, luego un botón para hacer clic en la parte inferior que muestra un mensaje.

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)

Una cosa interesante de notar es la forma en que puede hacer referencia a la entrada de my_edit diciendo MAIN_WINDOW.my_layout.my_edit.text . En la declaración de la ventana, creo que es importante poder nombrar arbitrariamente los controles en la función kwargs.

Aquí está la misma aplicación que usa solo posicionamiento absoluto (los controles aparecerán en diferentes lugares porque no estamos usando un administrador de diseño elegante):

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)

Todavía no estoy del todo seguro si este es un gran enfoque, pero definitivamente creo que está en el camino correcto. No tengo tiempo para explorar más esta idea, pero si alguien tomara esto como un proyecto, me encantaría.


Esto es extremadamente artificial y no pitónico en absoluto, pero aquí está mi intento de una traducción semi-literal usando la nueva declaración "con".

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

La parte más difícil es lidiar con el hecho de que Python no nos dará funciones anónimas con más de una declaración en ellos. Para evitar eso, podríamos crear una lista de comandos y ejecutarlos ...

De todos modos, aquí está el código de back-end con el que ejecuté esto:

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


Esto podría ser una simplificación excesiva, no creo que sea una buena idea intentar crear una biblioteca de UI de propósito general de esta manera. Por otro lado, podría utilizar este enfoque (metaclases y amigos) para simplificar la definición de ciertas clases de interfaces de usuario para una biblioteca ui existente y dependiendo de la aplicación que realmente podría ahorrarle una cantidad significativa de tiempo y líneas de código.


Si usa PyGTK con glade y esta capa de glade , entonces PyGTK en realidad se vuelve algo pitónico. Un poco al menos.

Básicamente, creas el diseño de GUI en Glade. También especifica devoluciones de eventos en glade. Luego escribes una clase para tu ventana como esta:

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")

Aquí, asumo que tengo un botón GTK en algún lugar llamado button1 y que especifiqué button_click_event como la devolución de llamada cliqueada . La capa de glade hace un gran esfuerzo al mapeo de eventos.

Si tuviera que diseñar una biblioteca Pythonic GUI, apoyaría algo similar, para ayudar al desarrollo rápido. La única diferencia es que me aseguraré de que los widgets tengan una interfaz más pitónica también. Las clases actuales de PyGTK me parecen muy C, excepto que utilizo foo.bar (...) en lugar de bar (foo, ...) aunque no estoy seguro de qué es exactamente lo que haría. Probablemente permita un medio declarativo del estilo de los modelos Django para especificar widgets y eventos en el código y permitiéndole acceder a datos a través de iteradores (donde tiene sentido, por ejemplo, listas de widgets), aunque realmente no lo he pensado.


## 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))

Sería relativamente fácil de hacer en Python con un poco de esa metaclass python magic know how. Que tengo Y un conocimiento de PyGTK. Lo cual también tengo. ¿Tiene ideas?


Tal vez no tan resbaladizo como la versión de Ruby, pero ¿qué tal algo así?

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!''

Como dijo Justin , para implementar esto necesitarás usar una metaclase personalizada en la App clase, y un montón de propiedades en Para y Button . Esto realmente no sería tan difícil.

El problema con el que te encuentras a continuación es: ¿cómo haces un seguimiento del orden en que aparecen las cosas en la definición de la clase? En Python 2.x, no hay manera de saber si t debe estar por encima de b o viceversa, ya que recibe el contenido de la definición de clase como un dict pitón.

Sin embargo, en Python 3.0 las metaclases se están cambiando de un par de formas (menores). Uno de ellos es el método __prepare__ , que le permite suministrar su propio objeto personalizado similar a un diccionario para ser utilizado en su lugar; esto significa que podrá seguir el orden en que se definen los elementos y colocarlos de acuerdo con la ventana .