[{"content": "Yams_ is  a pythonic way to describe an entity-relationship model. It is used at the core of the CubicWeb_ semantic web framework in order to automate lots of things, including the generation and validation of forms. Although we have been using the MVC_ design pattern to write user interfaces with Qt_ and Gtk_ before we started CubicWeb, we never got to reuse Yams. I am on my way to fix this.\r\n\r\n.. _Yams: http://www.logilab.org/project/yams\r\n.. _CubicWeb: http://www.cubicweb.org/\r\n.. _Qt: http://qt.nokia.com/products/\r\n.. _Gtk: http://www.gtk.org\r\n.. _MVC: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller\r\n\r\nHere is the simplest possible example that generates a user interface (using dialog_ and `python-dialog`_) to input data described by a Yams data model.\r\n\r\n.. _dialog: http://invisible-island.net/dialog/\r\n.. _`python-dialog`: http://pythondialog.sourceforge.net/\r\n\r\nFirst, let's write a function that builds the data model:\r\n\r\n.. sourcecode:: python\r\n\r\n  def mk_datamodel():\r\n      from yams.buildobjs import EntityType, RelationDefinition, Int, String\r\n      from yams.reader import build_schema_from_namespace\r\n\r\n      class Question(EntityType):\r\n          number = Int()\r\n          text = String()\r\n\r\n      class Form(EntityType):\r\n          title = String()\r\n\r\n      class in_form(RelationDefinition):\r\n          subject = 'Question'\r\n          object = 'Form'\r\n          cardinality = '*1'\r\n\r\n      return build_schema_from_namespace(vars().items())\r\n\r\nHere is what you get using graphviz_ or xdot_ to display the schema of that data model with:\r\n\r\n.. sourcecode:: python\r\n\r\n  import os\r\n  from yams import schema2dot\r\n\r\n  datamodel = mk_datamodel()\r\n  schema2dot.schema2dot(schema, '/tmp/toto.dot')\r\n  os.system('xdot /tmp/toto.dot')\r\n\r\n\r\n.. image:: http://www.logilab.org/file/87002?vid=download\r\n   :align: right\r\n\r\n\r\n.. _xdot: http://code.google.com/p/jrfonseca/wiki/XDot\r\n.. _graphviz: http://www.graphviz.org/\r\n\r\nTo make a step in the direction of genericity, let's add a class that abstracts the dialog API:\r\n\r\n.. sourcecode:: python\r\n\r\n  class InterfaceDialog:\r\n      \"\"\"Dialog-based Interface\"\"\"\r\n      def __init__(self, dlg):\r\n          self.dlg = dlg\r\n\r\n      def input_list(self, invite, options) :\r\n          assert len(options) != 0, str(invite)\r\n          choice = self.dlg.radiolist(invite, list=options, selected=1)\r\n          if choice is not None:\r\n              return choice.lower()\r\n          else:\r\n              raise Exception('operation cancelled')\r\n\r\n      def input_string(self, invite, default):\r\n          return self.dlg.inputbox(invite, init=default).decode(sys.stdin.encoding)\r\n\r\nAnd now let's put everything together:\r\n\r\n.. sourcecode:: python\r\n\r\n  datamodel = mk_datamodel()\r\n\r\n  import dialog\r\n  ui = InterfaceDialog(dialog.Dialog())\r\n  ui.dlg.setBackgroundTitle('Dialog Interface with Yams')\r\n\r\n  objs = []\r\n  for entitydef in datamodel.entities():\r\n      if entitydef.final:\r\n          continue\r\n      obj = {}\r\n      for attr in entitydef.attribute_definitions():\r\n          if attr[1].type in ('String','Int'):\r\n              obj[str(attr[0])] = ui.input_string('%s.%s' % (entitydef,attr[0]), '')\r\n      try:\r\n          entitydef.check(obj)\r\n      except Exception, exc:\r\n          ui.dlg.scrollbox(str(exc))\r\n\r\n  print objs\r\n\r\n.. image:: http://www.logilab.org/file/87001?vid=download\r\n   :align: right\r\n\r\nThe result is a program that will prompt the user for the title of a form and the text/number of a question, then enforce the type constraints and display the inconsistencies.\r\n\r\nThe above is very simple and does very little, but if you read the documentation of Yams and if you think about generating the UI with Gtk or Qt instead of dialog, or if you have used the form mechanism of CubicWeb, you'll understand that this proof of concept opens a door to a lot of possibilities.\r\n\r\nI will come back to this topic in a later article and give an example of integrating the above with pigg_, a simple MVC library for Gtk, to make the programming of user-interfaces even more declarative and bug-free.\r\n\r\n.. _pigg: http://www.logilab.org/project/pigg", "cwuri": "http://www.logilab.org/87003", "__cwetype__": "BlogEntry", "eid": 87003, "modification_date": "2012/01/09 14:22:12", "title": "Generating a user interface from a Yams model", "content_format": "text/rest", "creation_date": "2012/01/09 12:55:50"}]