كيف يمكنك تصميم إطار عمل واجهة مستخدم "بيثوني" للغاية؟

StackOverflow https://stackoverflow.com/questions/58711

سؤال

لقد كنت ألعب مع "أحذية" مكتبة روبي.يمكنك بشكل أساسي كتابة تطبيق واجهة المستخدم الرسومية بالطريقة التالية:

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

هذا جعلني أفكر - كيف يمكنني تصميم إطار عمل مماثل لطيف الاستخدام لواجهة المستخدم الرسومية في بايثون؟مكتبة لا تحتوي على الروابط المعتادة المتمثلة في كونها أغلفة لمكتبة C* (في حالة GTK وTk وwx وQT وما إلى ذلك)

تأخذ الأحذية الأشياء من تطوير الويب (مثل #f0c2f0 تدوين لون النمط، وتقنيات تخطيط CSS، مثل :margin => 10)، ومن روبي (باستخدام الكتل على نطاق واسع بطرق معقولة)

إن افتقار بايثون إلى "الكتل الياقوتية" يجعل المنفذ المباشر (مجازيًا) مستحيلًا:

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 label في المثال أعلاه) قد يكون معقدًا إلى حد ما، لكنه يبدو قابلاً للتنفيذ بطريقة أنيقة إلى حد ما..

هل كانت مفيدة؟

المحلول

يمكنك فعلًا تنفيذ ذلك، لكن الأمر سيتطلب استخدام الفئات الوصفية، وهي عميق السحر (يوجد تنانين).إذا كنت تريد مقدمة للفئات الوصفية، فهناك سلسلة من مقالات من آي بي إم التي تمكنت من تقديم الأفكار دون ذوبان عقلك.

قد يساعد الكود المصدري من ORM مثل SQLObject أيضًا، لأنه يستخدم نفس النوع من بناء الجملة التعريفي.

نصائح أخرى

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

سيكون من السهل نسبيًا القيام بذلك في لغة بايثون مع القليل من سحر بايثون metaclass الذي يعرف كيف.الذي لدي.ومعرفة PyGTK.والذي لدي أيضا.يحصل على أفكار؟

لم أكن راضيًا أبدًا عن مقالات ديفيد ميرتز في شركة IBM حول metaclsses، لذا قمت مؤخرًا بكتابة مقالتي الخاصة مقالة ميتا كلاس.يتمتع.

هذا أمر مفتعل للغاية وليس لغة بايثونية على الإطلاق، ولكن هذه هي محاولتي لترجمة شبه حرفية باستخدام عبارة "with" الجديدة.

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

مع بعض سحر Metaclass للحفاظ على الترتيب، لدي ما يلي.لست متأكدًا من مدى كونها لغة بايثونية ولكنها ممتعة جدًا لإنشاء أشياء بسيطة.

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

المحاولة الوحيدة للقيام بذلك التي أعرفها هي شمع هانز نواك (الذي مات للأسف).

أقرب ما يمكنك الوصول إليه من كتل الياقوت هو عبارة with من pep343:

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

إذا كنت تستخدم بيجتك مع الفسحة و هذا المجمع الفسحة, ، فإن 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 يسمى في مكان ما زر +1 والتي حددتها Button_click_event كما تم النقر عليه أتصل مرة أخرى.يستهلك غلاف الفسحة الكثير من الجهد في رسم خرائط الأحداث.

إذا كنت سأصمم مكتبة Pythonic GUI، فسأدعم شيئًا مشابهًا للمساعدة في التطوير السريع.والفرق الوحيد هو أنني سأضمن أن تحتوي الأدوات على واجهة أكثر لغة بايثونية أيضًا.تبدو فئات PyGTK الحالية C للغاية بالنسبة لي، باستثناء أنني أستخدم foo.bar(...) بدلاً من bar(foo, ...) على الرغم من أنني لست متأكدًا تمامًا مما سأفعله بشكل مختلف.من المحتمل أن تسمح لأسلوب نماذج Django بالوسائل التصريحية لتحديد عناصر واجهة المستخدم والأحداث في التعليمات البرمجية والسماح لك بالوصول إلى البيانات من خلال التكرارات (حيث يكون ذلك منطقيًا، على سبيل المثال، ربما قوائم عناصر واجهة المستخدم)، على الرغم من أنني لم أفكر في الأمر حقًا.

ربما لا يكون بنفس جودة إصدار روبي، ولكن ماذا عن شيء مثل هذا:

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

كما قال جاستن, لتنفيذ ذلك، ستحتاج إلى استخدام فئة تعريفية مخصصة في الفصل الدراسي App, ، ومجموعة من العقارات Para و Button.وهذا في الواقع لن يكون صعبا للغاية.

المشكلة التي تواجهك بعد ذلك هي:كيف يمكنك تتبع طلب أن الأشياء تظهر في تعريف الطبقة؟في Python 2.x، لا توجد طريقة لمعرفة ما إذا كان t يجب أن يكون أعلاه b أو العكس، حيث أنك تتلقى محتويات تعريف الفئة على أنها بيثون dict.

ومع ذلك، في بيثون 3.0 يتم تغيير الفئات الوصفية في عدة طرق (بسيطة).واحد منهم هو __prepare__ الطريقة، والتي تتيح لك توفير كائن مخصص يشبه القاموس لاستخدامه بدلاً من ذلك - وهذا يعني أنك ستتمكن من تتبع الترتيب الذي يتم به تعريف العناصر ووضعها وفقًا لذلك في النافذة.

قد يكون هذا تبسيطًا مبالغًا فيه، ولا أعتقد أنه سيكون من الجيد محاولة إنشاء مكتبة واجهة مستخدم للأغراض العامة بهذه الطريقة.من ناحية أخرى، يمكنك استخدام هذا الأسلوب (الفئات التعريفية والأصدقاء) لتبسيط تعريف فئات معينة من واجهات المستخدم لمكتبة واجهة مستخدم موجودة واعتمادًا على التطبيق الذي يمكن أن يوفر لك قدرًا كبيرًا من الوقت وأسطر التعليمات البرمجية.

لدي هذه المشكلة.أرغب في إنشاء غلاف حول أي مجموعة أدوات لواجهة المستخدم الرسومية لـ Python تكون سهلة الاستخدام ومستوحاة من الأحذية، ولكنها تحتاج إلى اتباع نهج OOP (مقابل كتل الياقوت).

مزيد من المعلومات في: http://wiki.alcidesfonseca.com/blog/python-universal-gui-revisited

مرحبا بأي شخص للانضمام إلى المشروع.

إذا كنت تريد حقًا ترميز واجهة المستخدم، فيمكنك محاولة الحصول على شيء مشابه لـ Django's ORM؛قم بهذا للحصول على متصفح مساعدة بسيط:

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

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

تتمثل الفكرة في تفسير بعض الحاويات (مثل النوافذ) كفئات بسيطة، وبعض الحاويات (مثل الجداول، وv/hboxes) التي يتم التعرف عليها بواسطة أسماء الكائنات، والأدوات البسيطة ككائنات.

لا أعتقد أنه سيتعين على المرء تسمية جميع الحاويات داخل النافذة، لذا فإن بعض الاختصارات (مثل التعرف على فئات النمط القديم كعناصر واجهة مستخدم حسب الأسماء) ستكون مرغوبة.

حول ترتيب العناصر:في MyWindow أعلاه، لا يتعين عليك تتبع هذا (النافذة عبارة عن حاوية ذات فتحة واحدة من الناحية النظرية).في الحاويات الأخرى، يمكنك محاولة تتبع الترتيب على افتراض أن كل منشئ عناصر واجهة المستخدم لديه حق الوصول إلى بعض قوائم عناصر واجهة المستخدم العامة.هذه هي الطريقة التي يتم بها ذلك في جانغو (AFAIK).

القليل من الاختراقات هنا، والقليل من التعديلات هناك...لا يزال هناك القليل من الأشياء التي يجب التفكير فيها، لكنني أعتقد أنه من الممكن ...وقابلة للاستخدام، طالما أنك لا تقوم بإنشاء واجهات مستخدم معقدة.

ومع ذلك أنا سعيد جدًا بـ PyGTK+Glade.واجهة المستخدم هي مجرد نوع من البيانات بالنسبة لي ويجب التعامل معها على أنها بيانات.هناك الكثير من المعلمات التي يجب تعديلها (مثل التباعد في أماكن مختلفة) ومن الأفضل إدارة ذلك باستخدام أداة واجهة المستخدم الرسومية.لذلك أقوم ببناء واجهة المستخدم الخاصة بي في الفسحة وحفظها بتنسيق xml وتحليلها باستخدام gtk.glade.XML().

التعريفي ليس بالضرورة أكثر (أو أقل) من لغة بايثونية من IMHO الوظيفية.أعتقد أن النهج متعدد الطبقات سيكون الأفضل (من الأسفل إلى الأعلى):

  1. طبقة أصلية تقبل وتعيد أنواع بيانات بايثون.
  2. طبقة ديناميكية وظيفية.
  3. طبقة تعريفية أو أكثر موجهة للكائنات.

مشابه ل إكسير + SQLAlchemy.

شخصيا، سأحاول التنفيذ مسج مثل 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'

فيما يلي طريقة تتعامل مع تعريفات واجهة المستخدم الرسومية بشكل مختلف قليلاً باستخدام البرمجة التعريفية القائمة على الفصل بدلاً من الميراث.

هذا مستوحى بشكل كبير من 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)

لست متأكدًا تمامًا حتى الآن مما إذا كان هذا نهجًا رائعًا للغاية، لكنني أعتقد بالتأكيد أنه يسير على الطريق الصحيح.ليس لدي الوقت لاستكشاف هذه الفكرة أكثر، ولكن إذا أخذ شخص ما هذا كمشروع، فسوف أحبه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top