2008-09-12 2 views
11

Я играл с библиотекой Ruby "shoes". В принципе вы можете написать приложение GUI следующим образом:Как бы вы разработали интерфейс «Pythonic» UI?

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 

Это заставило меня думать, - как бы я придумываю так же приятный в использовании рамки GUI в Python? Тот, у которого нет обычных привязок, в основном являющихся обертками в библиотеке C * (в случае GTK, Tk, wx, QT и т. Д.)

Обувь берет вещи из web devlopment (например, #f0c2f0 цветной обозначение стиля, методы компоновки CSS, как :margin => 10), а также из рубина (экстенсивно с использованием блоков в разумных путях)

отсутствие Пайтона из «rubyish блоков» делает (метафорический) -direct порта невозможно:

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) 

где рядом как чистый, и не будет почти как гибкий, и я даже не уверен, что это будет осуществимо.

Использование декораторов кажется, интересный способ отображения блоков кода для конкретного действия:

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 

В основном функция декоратор сохраняет функцию, а затем, когда произошло действие (скажем, щелчок) соответствующая функция будет выполнен.

Объем различного материала (скажем, la метка в приведенном выше примере) может быть довольно сложным, но, кажется выполнимой в довольно аккуратный образом ..

+1

И Ruby, и Python подходят для DSL. Разница заключается в «L»; это означает «языки» для Ruby и «библиотеки» для Python. Вы могли бы заставить некоторые синтаксические магии из Python, например, модели Django, но должны ли вы? – jfs 2008-12-03 05:19:06

ответ

7

Вы могли бы на самом деле осуществить это, но это было бы требуют использования метаклассов, которые являются deep magic (есть драконы). Если вы хотите введение в метаклассы, есть серия articles from IBM, которые умеют вводить идеи без таяния вашего мозга.

Исходный код ORM, например SQLObject, также может помочь, так как он использует такой же вид декларативного синтаксиса.

+1

_but_ для этого потребуются метаклассы? Метаклассы на самом деле не так сложны, как кажется на первый взгляд. См. Эту статью: http://www.voidspace.org.uk/python/articles/five-minutes.shtml – Dan 2008-12-03 09:12:41

2

Может быть, не так гладко, как версия Руби, но как о чем-то вроде этого:

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

Like Justin said, для реализации этого вам нужно будет использовать метакласса на классе App и кучей свойств на Para и Button. Это было бы не слишком сложно.

Проблема, с которой вы столкнулись, следующая: как вы отслеживаете заказ, что вещи появляются в определении класса? В Python 2.x нет способа узнать, должно ли t быть выше b или наоборот, так как вы получаете содержимое определения класса в виде питона dict.

Однако, в Python 3.0 metaclasses are being changed в нескольких (второстепенных) способами. Одним из них является метод __prepare__, который позволяет вам использовать собственный пользовательский объект, похожий на словарь, который будет использоваться вместо этого - это означает, что вы сможете отслеживать порядок, в котором определяются элементы, и соответствующим образом размещать их в окне ,

+0

На самом деле есть трюк, чтобы отслеживать порядок атрибутов. Посмотрите поля ORM Django (http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/__init__.py#L59). – 2008-09-13 15:42:56

+0

Не знаю - за исключением привязки магической кнопки, он очень похож на Tkinter. Я просто подожду, чтобы PyTTK был интегрирован в Python 2.7/3.1 (http://bugs.python.org/issue2983), поэтому вы получите собственный внешний вид со встроенным набором инструментальных средств. – Brandon 2008-12-02 20:09:33

2

Это может быть упрощением, я не думаю, что было бы неплохо попытаться сделать библиотеку UI общего назначения таким образом. С другой стороны, вы можете использовать этот подход (метаклассы и друзья), чтобы упростить определение определенных классов пользовательских интерфейсов для существующей ui-библиотеки и в зависимости от приложения, которое могло бы фактически сэкономить вам значительное количество времени и строк кода.

+0

«Облегчение упрощения обычно вызывает неприятные последствия»: тавтология. Обувь - это, безусловно, «упрощение» - погода - это «упрощение», зависит от ваших потребностей. Я бы сказал, что простое создание пользовательского интерфейса может быть очень полезным. – Neall 2008-12-02 18:31:57

3

С некоторой магией метакласса, чтобы сохранить заказ, у меня есть следующая работа. Я не уверен, как это pythonic, но это весело для создания простых вещей.

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

У меня есть эта проблема. Я хотел создать обертку вокруг любого инструментария GUI для Python, который прост в использовании и вдохновлен Обувьми, но должен быть подход ООП (против блоков ruby).

Больше информации в: прием http://wiki.alcidesfonseca.com/blog/python-universal-gui-revisited

Любой человек, чтобы присоединиться к проекту.

3

Единственная попытка сделать это, о которой я знаю, - Hans Nowak's Wax (что, к сожалению, мертво).

4

Я никогда не был доволен статьями Дэвида Мерц по адресу IBM на metaclsses, так что я недавно написал мой собственный metaclass article. Наслаждаться.

1

Если вы действительно хотите кодировать интерфейс, вы можете попытаться получить что-то похожее на ORM django; СТГ, как это, чтобы получить простую справку браузера:

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

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

Идея заключается в том, чтобы интерпретировать некоторые контейнеры (например, окна), как простые классы, некоторые контейнеры (например, таблицы, об/hboxes), распознаваемые имена объектов, и просто виджеты как объекты.

Я не думаю, что нужно было бы назвать все контейнеры внутри окна, поэтому некоторые ярлыки (например, классы старого стиля, которые будут признаны в виде виджетов по именам).

О порядке элементов: в MyWindow выше вам не нужно отслеживать это (окно концептуально представляет собой контейнер с одним слотом). В других контейнерах вы можете попытаться отслеживать заказ, предполагая, что каждый конструктор виджета имеет доступ к некоторому глобальному списку виджетов. Вот как это делается в джанго (AFAIK).

Немногие хаки здесь, несколько настроек там ... Есть еще мало вещей, о которых можно подумать, но я считаю, что это возможно ... и можно использовать, если вы не строите сложные пользовательские интерфейсы.

Однако я очень доволен PyGTK + Glade. Пользовательский интерфейс - это всего лишь данные для меня, и его следует рассматривать как данные. Есть слишком много параметров для настройки (например, расстояние в разных местах), и лучше управлять этим с помощью инструмента GUI. Поэтому я создаю свой пользовательский интерфейс в поляне, сохраняю как xml и parse с помощью gtk.glade.XML().

1

Декларативный не обязательно более (или менее) питонический, чем функциональный ИМХО. Я думаю, что многоуровневый подход будет лучшим (от buttom up):

  1. Нативный слой, который принимает и возвращает типы данных python.
  2. Функциональный динамический слой.
  3. Один или несколько декларативных/объектно-ориентированных слоев.

Похож на Elixir + SQLAlchemy.

1

Лично я бы попытался реализовать JQuery как API в графическом интерфейсе.

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

Это очень надуманный и не вещий вообще, но вот моя попытка полу-буквальный перевод с использованием новой «с» заявлением.

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

Самая трудная часть имеет дело с тем, что питон не даст нам анонимные функции с более чем одним утверждением в них. Чтобы обойти это, мы могли бы создать список команд и запустить через те ...

Во всяком случае, вот бэкенд код, который я побежал это с:

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 
1

Вот такой подход, который идет о GUI Определения бит по-разному, используя мета-программирование на основе классов, а не наследование.

Это большой Django/SQLAlchemy, вдохновленный тем, что он в значительной степени основан на метапрограммировании и отделяет ваш графический интерфейс от вашего «кодового кода». Я также думаю, что это должно сильно использовать менеджеров компоновки, таких как Java, потому что, когда вы бросаете код, никто не хочет постоянно настраивать выравнивание пикселей. Я также думаю, что было бы здорово, если бы у нас были CSS-подобные свойства.

Вот пример грубого мозгового штурма, в котором будет показан столбец с меткой сверху, затем текстовое поле, а затем кнопка, которая нажимает на нижнюю часть, которая показывает сообщение.

 
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) 

Одна прохладная вещь, чтобы заметить это, как вы можете ссылаться на вход my_edit говоря MAIN_WINDOW.my_layout.my_edit.text. В объявлении для окна, я думаю, что важно иметь возможность произвольно называть элементы управления в функции kwargs.

Здесь же приложение только с помощью абсолютного позиционирования (элементы управления будут появляться в разных местах, потому что мы не используем фантазии менеджера компоновки):

 
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) 

Я не совсем уверен, но если это супер отличный подход, но я определенно думаю, что это на правильном пути. У меня нет времени, чтобы исследовать эту идею больше, но если бы кто-то воспринял это как проект, я бы их полюбил.

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

Это было бы относительно легко сделать в python с небольшим количеством знаний о метаклассе python magic. Что у меня есть. И знание PyGTK. Который я также имею. Получает идеи?

3

Если вы используете PyGTK с glade и this glade wrapper, то PyGTK на самом деле становится несколько питоническим. По крайней мере, немного.

В принципе, вы создаете графический интерфейс в Glade. Вы также указываете обратные вызовы событий на поляне.Тогда вы пишете класс для окна, как это:

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

Здесь я предполагаю, что у меня есть кнопка GTK где-то под названием button1 и что я указал button_click_event как нажал обратного вызова. Обертка для полянов требует много усилий для сопоставления событий.

Если бы я должен был создать библиотеку Pythonic GUI, я бы поддержал нечто подобное, чтобы помочь быстрому развитию. Единственное различие заключается в том, что я бы удостоверился, что виджеты также имеют более питоновский интерфейс. Текущие классы PyGTK кажутся мне очень C, за исключением того, что я использую foo.bar (...) вместо bar (foo, ...), хотя я не уверен точно, что бы я сделал по-другому. Вероятно, разрешите декларативные средства стиля стиля Django для указания виджетов и событий в коде и позволяющие вам получать доступ к данным, хотя итераторы (где это имеет смысл, например, списки виджетов), хотя я об этом не думал.

Смежные вопросы