Single Responsibility Principle for CRUD operations against a collection - python

I'm trying to understand how to logically separate CRUD responsibilities so to adhere to the Single Responsibility Principle (SRP).
As I understand the definition of SRP, a single responsibility may not necessarily be a single behavior, but instead be a collection of behaviors with a well-defined, logical boundary from others.
In my example, RestaurantMenu is nothing more than a collection. I understand that there are more efficient ways to represent this, such as with a dictionary, but that is beyond the intent of this example. My RestaurantMenu has no behavior assigned to it because it remains unclear to me as to whether defining any further behavior by it would breach the SRP. It feels rather uncomfortable instantiating and calling separate CRUD objects through a Manager object rather than through methods in RestaurantMenu, so that is why I've decided to ask the audience here for some guidance.
Does the following example pass the SRP litmus test?
class RestaurantMenu(object):
def __init__(self, title, creator, catalog_type, restaurant):
self._title = title
self._creator = creator
self._catalog_type = catalog_type
self._restaurant = restaurant
self._menuitems = dict()
class MenuManager(object):
"""Responsibility
--------------
Coordinates CRUD related activities with a menu
"""
def __init__(self, menu):
self._menu = menu
def add_menu_item(self, item, value):
menu_item_adder = AddMenuItem(self._menu)
menu_item_adder(item, value)
def del_menu_item(self, item):
menu_item_deleter = DelMenuItem(self._menu)
menu_item_deleter(item)
def update_menu_item(self, existing_item, new_info):
menu_item_updater = UpdateMenuItem(self._menu)
menu_item_updater(existing_item, new_info)
def get_menu_items(self):
menu_item_getter = GetMenuItems(self._menu)
menu_item_getter()
class GetMenuItems(object):
def __init__(self, menu):
self._menu = menu
def __call__(self):
print(self._menu._title)
print('='*len(self._menu._title))
for key, value in self._menu._menuitems.items():
print(key, value)
class AddMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, item, value):
if item not in self._menu._menuitems:
self._menu._menuitems[item] = value
print('Item added:', item)
else:
print('Item already exists. Please update instead.')
class DelMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, item):
popped = self._menu._menuitems.pop(item)
print('Item removed:', popped)
class UpdateMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, existing_item, new_info):
self._menu._menuitems.update(existing_item=new_info)
print('Item updated:', existing_item, ' with', new_info)
def main():
mymenu = RestaurantMenu("Joe's Crab Shack 2014 Menu",
"Joe Schmoe",
"Restaurant",
"Joe's Crab Shack")
menumanager = MenuManager(mymenu)
menumanager.add_menu_item('longneck_clams', 7.00)
menumanager.add_menu_item('1 pound lobster', 15.00)
menumanager.add_menu_item('lobster chowder', 9.00)
print('-'*50)
menumanager.get_menu_items()
if __name__ == "__main__":
main()

One possible definition of SRP compliance is that there should only be one reason for a class to change.
This makes it very hard to call SRP or not on a piece of code in the abstract -- it basically depends on what will happen to evolve together and separately over time in your application.
Generally speaking though, the UI is one of the primary things that might evolve independently from other parts of a program. Users will keep wanting to make little display adjustments over the course of a project, and it's a nice thing to be able to modify the presentation logic without fearing to break the rest of the system. Persistence is another thing you might want to change, either as a result of new architectural decisions or temporarily, depending on the context (swapping in dummy persistence objects in tests for instance).
This is why in most real-world applications, I would tend to split up classes by technical responsibility rather than business operations on a same entity like C/R/U/D.
If you look closely at your current implementation, you'll notice patterns in your classes. They all fiddle with a MenuManager and the MenuItems stored in it. They all print things to the screen.
If you want to change something in the way data is displayed or stored, you'll basically have to touch all these classes. I'm not saying it's a serious flaw in the case of a small simple system like this, but in a larger application it might well be a problem.
Put another way, your example makes it easy to have menu updates done through a graphical interface into a SQL database, menu inserts done via a command shell into flat files, and menu reads spitting out an XML file with data gathered from a web service. This might be what you want to do in very particular circumstances, but not most of the time...

I just want to complement #guillaume31 answer, but I don't think it will fit in a comment.
As I understand the definition of SRP, a single responsibility may not necessarily be a single behavior, but instead be a collection of behaviors with a well-defined, logical boundary from others.
However you say you understand this, your code shows the opposite. You've spread a high cohesive group of tasks through several classes. Why this is bad?
How many times do you have the following code?
def __init__(self, menu):
self._menu = menu
I'm to lazy to count it, but you'll notice that this is an unecessary code duplication.
In this particular simple case, there is no problem, in deed, but if you application grow, you'll have a huge headache.
In some countries, it's valentine's day tomorrow, so you should remember how to KISS.

Related

How do you keep code consistent with multiple developers?

I know that Python is a dynamically typed language, and that I am likely trying to recreate Java behavior here. However, I have a team of people working on this code base, and my goal with the code is to ensure that they are doing things in a consistent manner. Let me give an example:
class Company:
def __init__(self, j):
self.locations = []
When they instantiate a Company object, an empty list that holds locations is created. Now, with Python anything can be added to the list. However, I would like for this list to only contain Location objects:
class Location:
def __init__(self, j):
self.address = None
self.city = None
self.state = None
self.zip = None
I'm doing this with classes so that the code is self documenting. In other words, "location has only these attributes". My goal is that they do this:
c = Company()
l = Location()
l.city = "New York"
c.locations.append(l)
Unfortunately, nothing is stopping them from simply doing c.locations.append("foo"), and nothing indicates to them that c.locations should be a list of Location objects.
What is the Pythonic way to enforce consistency when working with a team of developers?
An OOP solution is to make sure the users of your class' API do not have to interact directly with your instance attributes.
Methods
One approach is to implement methods which encapsulate the logic of adding a location.
Example
class Company:
def __init__(self, j):
self.locations = []
def add_location(self, location):
if isinstance(location, Location):
self.locations.append(location)
else:
raise TypeError("argument 'location' should be a Location object")
Properties
Another OOP concept you can use is a property. Properties are a simple way to define getter and setters for your instance attributes.
Example
Suppose we want to enforce a certain format for a Location.zip attribute
class Location:
def __init__(self):
self._zip = None
#property
def zip(self):
return self._zip
#zip.setter
def zip(self, value):
if some_condition_on_value:
self._zip = value
else:
raise ValueError('Incorrect format')
#zip.deleter
def zip(self):
self._zip = None
Notice that the attribute Location()._zip is still accessible and writable. While the underscore denotes what should be a private attribute, nothing is really private in Python.
Final word
Due to Python's high introspection capabilities, nothing will ever be totally safe. You will have to sit down with your team and discuss the tools and practice you want to adopt.
Nothing is really private in python. No class or class instance can
keep you away from all what's inside (this makes introspection
possible and powerful). Python trusts you. It says "hey, if you want
to go poking around in dark places, I'm gonna trust that you've got a
good reason and you're not making trouble."
After all, we're all consenting adults here.
--- Karl Fast
You could also define a new class ListOfLocations that make the safety checks. Something like this
class ListOfLocations(list):
def append(self,l):
if not isinstance(l, Location): raise TypeError("Location required here")
else: super().append(l)

PyQt obtaining collection of all registered fields in QWizard

I am working on a simple QWizard that displays some radio buttons on its pages. The buttons on a given page are all part of the same QButtonGroup. The page is registered as a custom field in itself, based on the selection in the button group:
class Page1(QWizardPage):
selectionChanged = pyqtSignal('QString')
def __init__(self, name):
self.group = QButtonGroup()
self.group.addButton(QRadioButton("a"))
self.group.addButton(QRadioButton("b"))
self.group.addButton(QRadioButton("c"))
self.registerField(name, self, 'selection', self.selectionChanged)
#pyqtProperty('QString')
def selection(self):
checkedButton = self.group.checkedButton()
return checkedButton.text() if checkedButton else None
def nextId(self): return -1
I end up registering self as the widget containing the field property simply because QButtonGroup is not a QWidget. All of the other pages look pretty much exactly like this (I am actually using base class to do all the common work, and this is just a minimal example).
I would like to be able to get a list of all the registered fields in the QWizard. I have not found any methods provided by Qt to allow me to do this so I made a workaround by overriding the behavior of each page's registerField method as well as the wizard's addPage:
def registerField(self, name, *args, **kwargs):
self.field_names.add(name)
if self.wizard() is not None:
self.wizard().field_names.add(name)
super().registerField(name, *args, **kwargs)
def addPage(self, page, *args, **kwargs):
self.field_names.union(page.field_names)
return super().addPage(page, *args, **kwargs)
I can then use the field_set attribute of the parent QWizard combined with QWizard.field to access all the values. This seems a bit redundant and therefore unnecessary. Is there a method in Qt to access the complete collection of fields? The relevant section in the documentation does not mention anything, but there are a lot of other details it omits, so that's not very telling.
My assumption is that the functionality, if it exists, would be the same for PyQt4 as for PyQt5. If it is not, I would prefer an answer for PyQt5 since that is what I am using at the moment.
You said that if the answer is negative it would have to be "pretty convincing." You admit that the documentation contains no mention of the function you want, and I will point out that no such function appears in the list of public functions for QWizard. Therefore the desired function, if it exists at all, is undocumented. To me, that consideration alone would be a "pretty convincing" reason not to use it. The next release of Qt might not have that function, or it might not work the same way.
Meanwhile you have an acceptable solution with eight lines of straightforward python code. Given the choice between that and calling an undocumented function (if you can find it), the python solution is vastly superior in all practical respects.
There is a potential problem with your Python code, however. You override the function QWizard.addPage, but there is another function QWizard.removePage that should probably be overridden as well. An alternative approach, which I would prefer, is not to store the field_names in QWizard at all but only in the individual pages. Add a method to QWizard to dynamically build a set of all the current field_names:
def all_field_names(self):
return {s for page_id in self.pageIds() for s in self.page(page_id).field_names}
[I didn't have a good way of testing this function, but I think you get the idea.] Now you remove the overridden method QWizard.addPage, remove the variable field_names from QWizard, and remove the middle two lines of register_field. Now you have only five lines of Python code, which will work regardless of how pages are added or removed. And you no longer store the field names in two places.
For what it's worth, whenever I'm confronted with a choice between using Qt's functionality or basic Python functionality, I always lean toward Python. I use threads instead of QThreads, threading locks and timers instead of Qt equivalents, Python method objects and callbacks instead of custom Slots and Signals. Qt was written for C++ programmers and that often means that it's not as "pythonic" as I would like.

Using static methods in python - best practice

When and how are static methods suppose to be used in python? We have already established using a class method as factory method to create an instance of an object should be avoided when possible. In other words, it is not best practice to use class methods as an alternate constructor (See Factory method for python object - best practice).
Lets say I have a class used to represent some entity data in a database. Imagine the data is a dict object containing field names and field values and one of the fields is an ID number that makes the data unique.
class Entity(object):
def __init__(self, data, db_connection):
self._data = data
self._db_connection
Here my __init__ method takes the entity data dict object. Lets say I only have an ID number and I want to create an Entity instance. First I will need to find the rest of the data, then create an instance of my Entity object. From my previous question, we established that using a class method as a factory method should probably be avoided when possible.
class Entity(object):
#classmethod
def from_id(cls, id_number, db_connection):
filters = [['id', 'is', id_number]]
data = db_connection.find(filters)
return cls(data, db_connection)
def __init__(self, data, db_connection):
self._data = data
self._db_connection
# Create entity
entity = Entity.from_id(id_number, db_connection)
Above is an example of what not to do or at least what not to do if there is an alternative. Now I am wondering if editing my class method so that it is more of a utility method and less of a factory method is a valid solution. In other words, does the following example comply with the best practice for using static methods.
class Entity(object):
#staticmethod
def data_from_id(id_number, db_connection):
filters = [['id', 'is', id_number]]
data = db_connection.find(filters)
return data
# Create entity
data = Entity.data_from_id(id_number, db_connection)
entity = Entity(data)
Or does it make more sense to use a standalone function to find the entity data from an ID number.
def find_data_from_id(id_number, db_connection):
filters = [['id', 'is', id_number]]
data = db_connection.find(filters)
return data
# Create entity.
data = find_data_from_id(id_number, db_connection)
entity = Entity(data, db_connection)
Note: I do not want to change my __init__ method. Previously people have suggested making my __init__ method to look something like this __init__(self, data=None, id_number=None) but there could be 101 different ways to find the entity data so I would prefer to keep that logic separate to some extent. Make sense?
When and how are static methods suppose to be used in python?
The glib answer is: Not very often.
The even glibber but not quite as useless answer is: When they make your code more readable.
First, let's take a detour to the docs:
Static methods in Python are similar to those found in Java or C++. Also see classmethod() for a variant that is useful for creating alternate class constructors.
So, when you need a static method in C++, you need a static method in Python, right?
Well, no.
In Java, there are no functions, just methods, so you end up creating pseudo-classes that are just bundles of static methods. The way to do the same thing in Python is to just use free functions.
That's pretty obvious. However, it's good Java style to look as hard as possible for an appropriate class to wedge a function into, so you can avoid writing those pseudo-classes, while doing the same thing is bad Python style—again, use free functions—and this is much less obvious.
C++ doesn't have the same limitation as Java, but many C++ styles are pretty similar anyway. (On the other hand, if you're a "Modern C++" programmer who's internalized the "free functions are part of a class's interface" idiom, your instincts for "where are static methods useful" are probably pretty decent for Python.)
But if you're coming at this from first principles, rather than from another language, there's a simpler way to look at things:
A #staticmethod is basically just a global function. If you have a function foo_module.bar() that would be more readable for some reason if it were spelled as foo_module.BazClass.bar(), make it a #staticmethod. If not, don't. That's really all there is to it. The only problem is building up your instincts for what's more readable to an idiomatic Python programmer.
And of course use a #classmethod when you need access to the class, but not the instance—alternate constructors are the paradigm case for that, as the docs imply. Although you often can simulate a #classmethod with a #staticmethod just by explicitly referencing the class (especially when you don't have much subclassing), you shouldn't.
Finally, getting to your specific question:
If the only reason clients ever need to look up data by ID is to construct an Entity, that sounds like an implementation detail you shouldn't be exposing, and it also makes client code more complex. Just use a constructor. If you don't want to modify your __init__ (and you're right that there are good reasons you might not want to), use a #classmethod as an alternate constructor: Entity.from_id(id_number, db_connection).
On the other hand, if that lookup is something that's inherently useful to clients in other cases that have nothing to do with Entity construction, it seems like this has nothing to do with the Entity class (or at least no more than anything else in the same module). So, just make it a free function.
The answer to the linked question specifically says this:
A #classmethod is the idiomatic way to do an "alternate constructor"—there are examples all over the stdlib—itertools.chain.from_iterable, datetime.datetime.fromordinal, etc.
So I don't know how you got the idea that using a classmethod is inherently bad. I actually like the idea of using a classmethod in your specific situation, as it makes following the code and using the api easy.
The alternative would be to use default constructor arguments like so:
class Entity(object):
def __init__(self, id, db_connection, data=None):
self.id = id
self.db_connection = db_connection
if data is None:
self.data = self.from_id(id, db_connection)
else:
self.data = data
def from_id(cls, id_number, db_connection):
filters = [['id', 'is', id_number]]
return db_connection.find(filters)
I prefer the classmethod version that you wrote originally however. Especially since data is fairly ambiguous.
Your first example makes the most sense to me: Entity.from_id is pretty succinct and clear.
It avoids the use of data in the next two examples, which does not describe what's being returned; the data is used to construct an Entity. If you wanted to be specific about the data being used to construct the Entity, then you could name your method something like Entity.with_data_for_id or the equivalent function entity_with_data_for_id.
Using a verb such as find can also be pretty confusing, as it doesn't give any indication of the return value — what is the function supposed to do when it's found the data? (Yes, I realize str has a find method; wouldn't it be better named index_of? But then there's also index...) It reminds me of the classic:
I always try to think what a name would indicate to someone with (a) no knowledge of the system, and (b) knowledge of other parts of the system — not to say I'm always successful!
Here is a decent use case for #staticmethod.
I have been working on a game as a side project. Part of that game includes rolling dice based on stats, and the possibility of picking up items and effects that impact your character's stats (for better or worse).
When I roll the dice in my game, I need to basically say... take the base character stats and then add any inventory and effect stats into this grand netted figure.
You can't take these abstract objects and add them without instructing the program how. I'm not doing anything at the class level or instance level either. I didn't want to define the function in some global module. The last best option was to go with a static method for adding up stats together. It just makes the most sense this way.
class Stats:
attribs = ['strength', 'speed', 'intellect', 'tenacity']
def __init__(self,
strength=0,
speed=0,
intellect=0,
tenacity=0
):
self.strength = int(strength)
self.speed = int(speed)
self.intellect = int(intellect)
self.tenacity = int(tenacity)
# combine adds stats objects together and returns a single stats object
#staticmethod
def combine(*args: 'Stats'):
assert all(isinstance(arg, Stats) for arg in args)
return_stats = Stats()
for stat in Stats.attribs:
for _ in args:
setattr(return_stats, stat,
getattr(return_stats, stat) + getattr(_, stat))
return (return_stats)
Which would make the stat combination calls work like this
a = Stats(strength=3, intellect=3)
b = Stats(strength=1, intellect=-1)
c = Stats(tenacity=5)
print(Stats.combine(a, b, c).__dict__)
{'strength': 4, 'speed': 0, 'intellect': 2, 'tenacity': 5}

OO design in python. Up across and down?

My question is really about how two objects that have both been created by a parent class can talk to each other. The real use case I have is for a PySide GUI application where two widgets that are sitting on a CentralWidget need to connect a signal and slot.
However the problem is more generic than that so I have a silly example to demonstrate the problem.
In particular is the "asksister" method in Son, where I know that the parent has a daughter and I need to connect those two methods. However, by hard linking the connection, means I can't run unittests without creating the entire object structure.
Is there a better way?
Would it be just less lame if I posted my real code here (after simplifying)
class Parent(object):
def __init__(self):
self.son = Son(self)
self.daughter = Daughter(self)
self.son.dohomework()
class Son(object):
def __init__(self, parent):
self.parent = parent
def dohomework(self):
print 'The answer is {0}'.format(self.asksister())
def asksister(self):
return self.parent.daughter.answer()
class Daughter(object):
def __init__(self, parent):
self.parent = parent
def answer(self):
return 42
if __name__ == '__main__':
parent = Parent()
I think that it is difficult to answer conclusively with a generic example such as the one you present. The point is that these two objects must interact, and their layout and communication depends on the domain and the entities they represent. Different design strategies may be equally valid, one fitting better than another for a specific case.
If it was on me, I would register the sister and the brother, leaving the parent out of the communication, but again, this is one design. If sister and brother were a view and a model, and the parent a controller, I would instead have them both talk only to the controller.
Another alternative would be to have Son request a helpService, where the sister previously registered as helpProvider. This way, the son must know the helpService, and gets the sister indirectly.

How would you design a very "Pythonic" UI framework?

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!"
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
This made me think - how would I design a similarly nice-to-use GUI framework in Python? One that doesn't have the usual tyings of basically being wrappers to a C* library (In the case of GTK, Tk, wx, QT etc etc)
Shoes takes things from web devlopment (like #f0c2f0 style colour notation, CSS layout techniques, like :margin => 10), and from ruby (extensively using blocks in sensible ways)
Python's lack of "rubyish blocks" makes a (metaphorically)-direct port impossible:
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)
No where near as clean, and wouldn't be nearly as flexible, and I'm not even sure if it would be implementable.
Using decorators seems like an interesting way to map blocks of code to a specific action:
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
Basically the decorator function stores the function, then when the action occurred (say, a click) the appropriate function would be executed.
The scope of various stuff (say, the la label in the above example) could be rather complicated, but it seems doable in a fairly neat manner..
You could actually pull this off, but it would require using metaclasses, which are deep magic (there be dragons). If you want an intro to metaclasses, there's a series of articles from IBM which manage to introduce the ideas without melting your brain.
The source code from an ORM like SQLObject might help, too, since it uses this same kind of declarative syntax.
I was never satisfied with David Mertz's articles at IBM on metaclsses so I recently wrote my own metaclass article. Enjoy.
This is extremely contrived and not pythonic at all, but here's my attempt at a semi-literal translation using the new "with" statement.
with Shoes():
t = Para("Not clicked!")
with Button("The Label"):
Alert("You clicked the button!")
t.replace("Clicked!")
The hardest part is dealing with the fact that python will not give us anonymous functions with more than one statement in them. To get around that, we could create a list of commands and run through those...
Anyway, here's the backend code I ran this with:
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
## 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?
With some Metaclass magic to keep the ordering I have the following working. I'm not sure how pythonic it is but it is good fun for creating simple things.
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()
The only attempt to do this that I know of is Hans Nowak's Wax (which is unfortunately dead).
The closest you can get to rubyish blocks is the with statement from pep343:
http://www.python.org/dev/peps/pep-0343/
If you use PyGTK with glade and this glade wrapper, then PyGTK actually becomes somewhat pythonic. A little at least.
Basically, you create the GUI layout in Glade. You also specify event callbacks in glade. Then you write a class for your window like this:
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")
Here, I'm assuming that I have a GTK Button somewhere called button1 and that I specified button_click_event as the clicked callback. The glade wrapper takes a lot of effort out of event mapping.
If I were to design a Pythonic GUI library, I would support something similar, to aid rapid development. The only difference is that I would ensure that the widgets have a more pythonic interface too. The current PyGTK classes seem very C to me, except that I use foo.bar(...) instead of bar(foo, ...) though I'm not sure exactly what I'd do differently. Probably allow for a Django models style declarative means of specifying widgets and events in code and allowing you to access data though iterators (where it makes sense, eg widget lists perhaps), though I haven't really thought about it.
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.
This could be an oversimplification, i don't think it would be a good idea to try to make a general purpose ui library this way. On the other hand you could use this approach (metaclasses and friends) to simplify the definition of certain classes of user interfaces for an existing ui library and depending of the application that could actually save you a significant amount of time and code lines.
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.
If you really want to code UI, you could try to get something similar to django's ORM; sth like this to get a simple help browser:
class MyWindow(Window):
class VBox:
entry = Entry()
bigtext = TextView()
def on_entry_accepted(text):
bigtext.value = eval(text).__doc__
The idea would be to interpret some containers (like windows) as simple classes, some containers (like tables, v/hboxes) recognized by object names, and simple widgets as objects.
I dont think one would have to name all containers inside a window, so some shortcuts (like old-style classes being recognized as widgets by names) would be desirable.
About the order of elements: in MyWindow above you don't have to track this (window is conceptually a one-slot container). In other containers you can try to keep track of the order assuming that each widget constructor have access to some global widget list. This is how it is done in django (AFAIK).
Few hacks here, few tweaks there... There are still few things to think of, but I believe it is possible... and usable, as long as you don't build complicated UIs.
However I am pretty happy with PyGTK+Glade. UI is just kind of data for me and it should be treated as data. There's just too much parameters to tweak (like spacing in different places) and it is better to manage that using a GUI tool. Therefore I build my UI in glade, save as xml and parse using gtk.glade.XML().
Personally, I would try to implement JQuery like API in a 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'
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.
Declarative is not necessarily more (or less) pythonic than functional IMHO. I think a layered approach would be the best (from buttom up):
A native layer that accepts and returns python data types.
A functional dynamic layer.
One or more declarative/object-oriented layers.
Similar to Elixir + SQLAlchemy.

Categories

Resources