13

私はRubyライブラリの「靴」で遊んでいます。基本的に、次の方法でGUIアプリケーションを作成できます。

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

これは私に考えさせられました-Pythonで同様に使いやすいGUIフレームワークをどのように設計しますか?基本的にC*ライブラリのラッパーであるという通常の結びつきがないもの(GTK、Tk、wx、QTなどの場合)

靴は、Web開発(#f0c2f0スタイルの色表記、CSSレイアウト手法など:margin => 10)とruby(賢明な方法でブロックを広範に使用する)から物事を取り入れます

Pythonには「ごちゃごちゃしたブロック」がないため、(比喩的に)直接移植は不可能です。

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、上記の例のラベル)の範囲はかなり複雑になる可能性がありますが、かなりきちんと実行できるようです。

4

15 に答える 15

7

実際にこれをやってのけることができますが、それには深い魔法であるメタクラスを使用する必要があります (ドラゴンが存在します)。メタクラスの紹介が必要な場合は、IBM からの一連の記事があり、脳を溶かすことなくアイデアを紹介しています。

SQLObject のような ORM からのソース コードも、同じ種類の宣言構文を使用するため、役立つ可能性があります。

于 2008-09-12T13:13:57.947 に答える
4

これは非常に不自然であり、Pythonic ではありませんが、新しい "with" ステートメントを使用した半文字変換の試みを次に示します。

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

最も難しい部分は、Python が複数のステートメントを含む無名関数を提供しないという事実に対処することです。これを回避するには、コマンドのリストを作成して、それらを実行することができます...

とにかく、これを実行したバックエンドコードは次のとおりです。

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
于 2008-12-03T00:15:11.567 に答える
4
## 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 で行うのは比較的簡単です。私が持っているもの。PyGTK の知識。私も持っています。アイデアを得る?

于 2008-12-03T08:48:06.617 に答える
4

メタクラスに関するIBM の David Mertz の記事に満足できなかったため、最近、独自のメタクラスの記事を書きました。楽しみ。

于 2008-12-02T19:30:23.107 に答える
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()
于 2008-09-15T13:18:54.633 に答える
3

Gladeとこの Glade wrapperPyGTKを使用すると、PyGTK は実際にはいくぶん Pythonic になります。少なくとも少し。

基本的に、Glade で GUI レイアウトを作成します。また、空き地でイベント コールバックを指定します。次に、ウィンドウのクラスを次のように記述します。

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

ここでは、 button1という名前の GTK ボタンがあり、クリックされたコールバックとして button_click_event を指定したと仮定ます。グレード ラッパーは、イベント マッピングに多大な労力を費やします。

Pythonic GUI ライブラリを設計する場合、迅速な開発を支援するために同様のものをサポートします。唯一の違いは、ウィジェットにもより Pythonic なインターフェイスがあることを確認することです。現在の PyGTK クラスは、bar(foo, ...) の代わりに foo.bar(...) を使用していることを除いて、私には非常に C に見えますが、どうすれば違うのか正確にはわかりません。おそらく、コードでウィジェットとイベントを指定し、イテレータを介してデータにアクセスできるようにするDjangoモデルスタイルの宣言的手段を可能にします(おそらくウィジェットリストなど、理にかなっています)が、私はそれについてあまり考えていません.

于 2008-12-03T09:18:40.797 に答える
3

rubyish ブロックに最も近いのは、pep343 の with ステートメントです。

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

于 2008-12-02T19:09:45.240 に答える
3

私が知っている唯一の試みは、Hans Nowak's Wax (残念ながら死んでいます) です。

于 2008-12-02T18:23:10.670 に答える
2

Ruby バージョンほど滑らかではないかもしれませんが、次のようなものはどうでしょうか。

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

Justin が言ったようAppに、これを実装するには、 classでカスタム メタクラスを使用し、 および で一連のプロパティを使用する必要がありParaますButton。これは実際にはそれほど難しいことではありません。

次に出くわす問題は、クラス定義に表示される順序をどのように追跡するかということです。Python 2.x では、クラス定義の内容を python として受け取るため、 をt上に置くか、その逆にするかを知る方法はありません。bdict

ただし、Python 3.0 では、メタクラスがいくつかの (マイナーな) 方法で変更されています。そのうちの 1 つは__prepare__メソッドで、独自のカスタム辞書のようなオブジェクトを代わりに使用できるようにします。これは、アイテムが定義されている順序を追跡し、それに応じてウィンドウに配置できることを意味します。

于 2008-09-12T13:40:35.527 に答える
2

これは単純化しすぎている可能性があります。この方法で汎用の ui ライブラリを作成しようとするのは良い考えではないと思います。一方、このアプローチ (メタクラスとその仲間) を使用して、既存の ui ライブラリのユーザー インターフェイスの特定のクラスの定義を簡素化し、アプリケーションに応じて、実際にかなりの時間とコード行を節約できます。

于 2008-09-13T14:20:42.083 に答える
1

個人的には、GUIフレームワークにAPIのようなJQueryを実装しようと思います。

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'
于 2008-12-02T21:03:49.173 に答える
1

これは、継承ではなくクラスベースのメタプログラミングを使用して、GUI 定義を少し異なる方法で行うアプローチです。

これは、メタプログラミングに大きく基づいており、GUIコードを「コードコード」から分離しているという点で、大規模なDjango/SQLAlchemyに触発されたものです。また、Java のようにレイアウト マネージャーを多用する必要があると考えています。また、CSS ライクなプロパティがあればいいと思います。

これは大まかなブレインストーミングの例で、上部にラベルがあり、テキスト ボックスがあり、下部にメッセージを表示するボタンをクリックします。

happygui.controlsインポートから *

MAIN_WINDOW = Window(width="500px", height="350px",
    my_layout=ColumnLayout(padding="10px",
        my_label=Lab​​el(text="キッド君の名前は?", 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): # handlers.py ファイルに簡単に入れることができます
    名前 = MAIN_WINDOW.my_layout.my_edit.text
    # 同じこと: name = sender.parent.my_edit.text
    # ベスト プラクティス、構造変更の影響を受けない: MAIN_WINDOW.find('my_edit').text
    MessageBox("あなたの名前は '%s'" % ()).show(modal=True)

注目すべきクールな点の 1 つは、 my_edit の入力を参照する方法ですMAIN_WINDOW.my_layout.my_edit.text。ウィンドウの宣言では、関数 kwargs でコントロールに任意の名前を付けられることが重要だと思います。

絶対配置のみを使用する同じアプリを次に示します (派手なレイアウト マネージャーを使用していないため、コントロールは別の場所に表示されます)。

happygui.controlsインポートから *

MAIN_WINDOW = Window(width="500px", height="350px",
    my_label=Lab​​el(text="キド君の名前は?", 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): # handlers.py ファイルに簡単に入れることができます
    名前 = MAIN_WINDOW.my_edit.text
    # 同じこと: name = sender.parent.my_edit.text
    # ベスト プラクティス、構造変更の影響を受けない: MAIN_WINDOW.find('my_edit').text
    MessageBox("あなたの名前は '%s'" % ()).show(modal=True)

これが非常に優れたアプローチであるかどうかはまだ完全にはわかりませんが、正しい道を進んでいることは間違いありません。このアイデアをこれ以上探求する時間はありませんが、もし誰かがこれをプロジェクトとして取り上げてくれたら、私は彼らが大好きです.

于 2008-12-03T02:37:06.600 に答える
1

本当に UI をコーディングしたい場合は、django の ORM に似たものを手に入れることができます。簡単なヘルプ ブラウザを表示するには、次のように入力します。

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

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

いくつかのコンテナー (ウィンドウなど) を単純なクラスとして解釈し、一部のコンテナー (テーブル、v/hbox など) をオブジェクト名で認識し、単純なウィジェットをオブジェクトとして解釈するという考え方です。

ウィンドウ内のすべてのコンテナーに名前を付ける必要はないと思うので、いくつかのショートカット (名前によってウィジェットとして認識される古いスタイルのクラスなど) が望ましいでしょう。

要素の順序について: 上記の MyWindow では、これを追跡する必要はありません (window は概念的には 1 スロットのコンテナーです)。他のコンテナーでは、各ウィジェット コンストラクターが何らかのグローバル ウィジェット リストにアクセスできると仮定して、順序を追跡することができます。これは、django(AFAIK)で行われる方法です。

ここにはいくつかのハック、そこにはいくつかの調整があります...考えるべきことはまだほとんどありませんが、複雑なUIを構築しない限り、それは可能であり、使用可能であると信じています.

しかし、私は PyGTK+Glade にかなり満足しています。UIは私にとって単なるデータであり、データとして扱われるべきです. 微調整するパラメーターが多すぎます (さまざまな場所の間隔など)。GUI ツールを使用して管理することをお勧めします。したがって、UI をグレードで構築し、xml として保存して、gtk.glade.XML() を使用して解析します。

于 2008-12-02T20:37:49.680 に答える
1

私はこれと同じ問題を抱えています。使いやすく、Shoes に触発された、Python 用の GUI ツールキットのラッパーを作成したいと考えていますが、OOP アプローチ (ルビー ブロックに対して) である必要があります。

詳細: http://wiki.alcidesfonseca.com/blog/python-universal-gui-revisited

プロジェクトへの参加はどなたでも大歓迎です。

于 2008-12-02T17:48:06.700 に答える
0

宣言型は、必ずしも機能的な IMHO よりも Pythonic であるとは限りません。階層化されたアプローチが最適だと思います(下から上へ):

  1. Python データ型を受け入れて返すネイティブ レイヤー。
  2. 機能的な動的レイヤー。
  3. 1 つ以上の宣言型/オブジェクト指向レイヤー。

Elixir + SQLAlchemyに似ています。

于 2008-12-02T20:48:23.247 に答える