Pergunta

Eu tenho jogado com o Ruby biblioteca "sapatos".Basicamente, você pode escrever um aplicativo GUI da seguinte forma:

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

Isso fez-me pensar - como é que eu desenho um da mesma forma agradável de usar GUI framework em Python?Aquele que não tem o costume tyings de, basicamente, wrappers para um C* biblioteca (No caso do GTK, Tk, wx, QT, etc, etc)

Calçado leva as coisas a partir da web desenvolvimento (como #f0c2f0 estilo cor de notação, CSS técnicas de layout, como :margin => 10), e a partir de ruby (extensivamente o uso de blocos em formas sensatas)

Python falta de "rubyish blocos" faz um (metaforicamente)-direto da porta impossível:

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)

Nenhum onde perto de tão limpo, e não seria quase como flexível, e eu nem tenho certeza se ele seria implementável.

Usando decoradores parece ser um interessante caminho para mapear blocos de código para uma acção 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

Basicamente, o decorador função armazena a função e, em seguida, quando a ação ocorreu (por exemplo, um clique) a função adequada seria executada.

O âmbito de várias coisas (digamos, o la etiqueta no exemplo acima) pode ser bastante complicado, mas parece factível em uma forma bastante elegante..

Foi útil?

Solução

Você pode puxar esta off, mas que exigiriam o uso de metaclasses, que são profundidade magia (there be dragons).Se você quer uma intro para metaclasses, há uma série de artigos da IBM que conseguem introduzir as idéias sem derreter o seu cérebro.

O código-fonte a partir de um ORM como o SQLObject pode ajudar, também, uma vez que ele utiliza esse mesmo tipo de sintaxe declarativa.

Outras dicas

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

Seria relativamente fácil para fazer em python com um pouco do que metaclasse python magia saber como.O que eu tenho.E o conhecimento do PyGTK.Que eu também tenho.Fica ideias?

Eu nunca estava satisfeito com David Mertz artigos na IBM em metaclsses então eu recentemente escrevi a minha própria metaclasse artigo.Desfrute.

Isso é extremamente forçado e não pythonic em tudo, mas aqui está a minha tentativa de um semi-tradução literal usando o novo "com" instrução.

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

A parte mais difícil é lidar com o fato de que o python não nos dará anônimo funções com mais do que uma declaração em si.Para isso, podemos criar uma lista de comandos e executar os...

De qualquer maneira, aqui está o código backend eu corri com este:

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

Com alguns Metaclasse magia para manter a ordem eu tenho o seguinte trabalho.Eu não tenho certeza de como pythonic é, mas é uma boa diversão para a criação de coisas 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()

A única tentativa de fazer isso, que eu saiba, é Hans Nowak Cera (que infelizmente está morto).

O mais próximo que você pode chegar ao rubyish blocos é a declaração de pep343:

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

Se você usar PyGTK com glade e este glade wrapper, e , em seguida, PyGTK, na verdade, torna-se um pouco pythonic.Um pouco pelo menos.

Basicamente, você cria o GUI layout em Glade.Você também especificar retornos de chamada de evento em glade.Em seguida, você escreve uma classe para a sua janela 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")

Aqui, eu estou supondo que eu tenha uma GTK Botão em algum lugar chamado button1 e que eu especificado button_click_event como o clicado de retorno de chamada.O glade wrapper exige um grande esforço de evento de mapeamento.

Se eu fosse desenhar um Pythonic GUI library, eu seria a favor de algo semelhante, para ajudar o desenvolvimento rápido.A única diferença é que eu iria garantir que os widgets mais pythonic interface muito.O atual PyGTK classes parecem muito C para mim, só que eu uso foo.bar(...) em vez de barra(foo, ...) embora eu não tenho certeza exatamente o que eu faria de forma diferente.Provavelmente permitir uma Django modelos estilo declarativo meio de especificar widgets e eventos em código e permitindo o acesso aos dados de que os iteradores (onde faz sentido, por exemplo, o widget lista, talvez), embora eu realmente não pensei sobre isso.

Talvez não tão liso como o Ruby versão, mas como é algo como isto:

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 o Justin disse:, para implementar isso, você precisará usar um personalizado metaclasse na classe App, e um monte de propriedades em Para e Button.Este, na verdade, não seria muito difícil.

O problema de você correr para a próxima é:como você a manter o controle do fim que as coisas aparecem na definição de classe?No Python 2.x, não há nenhuma maneira de saber se t deve ser acima de b ou o contrário, uma vez que você receber o conteúdo da definição de classe, como python dict.

No entanto, em Python 3.0 metaclasses estão sendo alteradas em algumas (pequenas) maneiras.Um deles é o __prepare__ método, que permite que você para fornecer o seu próprio dicionário personalizado-como o objeto a ser usado em vez -- isso significa que você vai ser capaz de controlar a ordem na qual os itens são definidos, e posicioná-los adequadamente na janela.

Esta poderia ser uma simplificação exagerada, eu não acho que seria uma boa idéia para tentar fazer uma finalidade geral biblioteca de interface desta forma.Por outro lado, você pode usar essa abordagem (metaclasses e amigos) para simplificar a definição de determinadas classes de interfaces do usuário para uma interface de usuário existente biblioteca e dependendo da aplicação que pode realmente economizar uma quantidade significativa de tempo e linhas de código.

Eu tenho esse mesmo problema.Eu wan para criar um wrapper em torno de qualquer kit de ferramentas de interface para a linguagem Python, que é fácil de usar, e inspirado por Sapatos, mas precisa ser uma abordagem OOP (contra ruby blocos).

Mais informações em: http://wiki.alcidesfonseca.com/blog/python-universal-gui-revisited

Qualquer pessoa é bem-vindo para participar do projeto.

Se você realmente deseja código de INTERFACE do usuário, você pode tentar obter algo semelhante ao django ORM;hts como este para fazer uma simples navegador de ajuda:

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

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

A idéia seria a de interpretar alguns recipientes (como o windows) como classes simples, alguns recipientes (como tabelas, v/hboxes) reconhecido por nomes de objeto, simples e widgets como objetos.

Eu não acho que teria o nome de todos os recipientes dentro de uma janela, de modo que alguns atalhos (como o velho estilo de classes a ser reconhecidos como elementos através de nomes) seria desejável.

Sobre a ordem dos elementos:em MyWindow acima, você não tem que controlar isso (janela é conceitualmente um slot recipiente).Em outros recipientes que você pode tentar manter o controle da ordem partindo do princípio de que cada elemento construtor de ter acesso a algumas global lista de widget.Esta é a forma como ele é feito em django (AFAIK).

Alguns hacks aqui, alguns ajustes lá...Ainda há algumas coisas para pensar, mas eu acredito que é possível...e utilizável, enquanto você não criar complicado UIs.

No entanto, estou muito feliz com PyGTK+Glade.INTERFACE do usuário é apenas um tipo de dados para mim e deve ser tratada como dados.Há muito de parâmetros para ajustar (como o espaçamento em lugares diferentes) e é melhor para gerenciar que o uso de uma ferramenta de GUI.Portanto, eu construir a minha INTERFACE do usuário em glade, salvar como xml e analisar utilizando gtk.glade.XML().

Declarativa não é, necessariamente, mais (ou menos) pythonic do que funcional, IMHO.Eu acho que uma abordagem em camadas que seria o melhor (a partir de buttom up):

  1. Um nativo da camada que aceita e retorna python tipos de dados.
  2. Uma dinâmica funcional camada.
  3. Um ou mais declarativa/object-oriented camadas.

Semelhantes Elixir + O SQLAlchemy.

Pessoalmente, eu gostaria de tentar implementar JQuery como a API em uma GUI framework.

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'

Aqui está uma abordagem que vai sobre o GUI definições de uma forma um pouco diferente usando a classe-base de meta-programação em vez de herança.

Este é largley Django/SQLAlchemy inspirado no que é fortemente baseado em meta-programação e separa o seu código GUI do seu "código".Eu também acho que ele deve fazer uso pesado de layout gestores como Java, porque quando você está largando código, ninguém quer constantemente ajustar pixel de alinhamento.Eu também acho que seria legal se pudéssemos ter como CSS propriedades.

Aqui está um áspero planejou um exemplo que vai mostrar uma coluna com uma etiqueta na parte superior e, em seguida, uma caixa de texto e, em seguida, um botão para clicar na parte inferior, que mostra uma mensagem.

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)

Uma coisa legal a se observar é a maneira que você pode fazer referência a entrada de my_edit dizendo: MAIN_WINDOW.my_layout.my_edit.text.Em declaração para a janela, eu acho que é importante ser capaz de, arbitrariamente, controles de nome em função kwargs.

Aqui é o mesmo app apenas usando posicionamento absoluto (a controls irá aparecer em lugares diferentes, porque nós não estamos usando uma fantasia de 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)

Eu não sou inteiramente certo ainda se isso é uma super grande aproximação, mas eu definitivamente acho que ele está no caminho certo.Eu não tenho tempo para explorar essa idéia mais, mas se alguém tomou isso como um projeto, eu gostaria que eles.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top