I have been playing with the Ruby library \"shoes\". Basically you can write a GUI application in the following way:
Shoes.app do
t = para \"Not clicked!\"
b
I have this same problem. I wan to to create a wrapper around any GUI toolkit for Python that is easy to use, and inspired by Shoes, but needs to be a OOP approach (against ruby blocks).
More information in: http://wiki.alcidesfonseca.com/blog/python-universal-gui-revisited
Anyone's welcome to join the project.
Here's an approach that goes about GUI definitions a bit differently using class-based meta-programming rather than inheritance.
This is largley Django/SQLAlchemy inspired in that it is heavily based on meta-programming and separates your GUI code from your "code code". I also think it should make heavy use of layout managers like Java does because when you're dropping code, no one wants to constantly tweak pixel alignment. I also think it would be cool if we could have CSS-like properties.
Here is a rough brainstormed example that will show a column with a label on top, then a text box, then a button to click on the bottom which shows a message.
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)
One cool thing to notice is the way you can reference the input of my_edit by saying MAIN_WINDOW.my_layout.my_edit.text
. In the declaration for the window, I think it's important to be able to arbitrarily name controls in the function kwargs.
Here is the same app only using absolute positioning (the controls will appear in different places because we're not using a fancy 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)
I'm not entirely sure yet if this is a super great approach, but I definitely think it's on the right path. I don't have time to explore this idea more, but if someone took this up as a project, I would love them.
The only attempt to do this that I know of is Hans Nowak's Wax (which is unfortunately dead).
## 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))
It would be relatively easy to do in python with a bit of that metaclass python magic know how. Which I have. And a knowledge of PyGTK. Which I also have. Gets ideas?
Maybe not as slick as the Ruby version, but how about something like this:
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, to implement this you would need to use a custom metaclass on class App
, and a bunch of properties on Para
and Button
. This actually wouldn't be too hard.
The problem you run into next is: how do you keep track of the order that things appear in the class definition? In Python 2.x, there is no way to know if t
should be above b
or the other way around, since you receive the contents of the class definition as a python dict
.
However, in Python 3.0 metaclasses are being changed in a couple of (minor) ways. One of them is the __prepare__
method, which allows you to supply your own custom dictionary-like object to be used instead -- this means you'll be able to track the order in which items are defined, and position them accordingly in the window.
Declarative is not necessarily more (or less) pythonic than functional IMHO. I think a layered approach would be the best (from buttom up):
Similar to Elixir + SQLAlchemy.