UIManager
Descripción del Problema
La creación de interfaces gráficas se torna en una tarea tediosa, dada la cantidad de código repetitivo que se debe generar. Las barras de menús y las barras de herramientas de una interfaz gráfica son ejemplos concretos de ello, ya que en wxpython, es necesario manualmente insertar los diferentes elementos que componen un menú y una barra de herramientas.
Esta librería esat basada en el UIManager de GTK+ (http://www.pygtk.org/pygtk2tutorial/sec-UIManager.html), que permite describir las barras de herramientas y las barras de menús de una aplicación usando XML, y permite realizar mezclas entre diferentes descripciones XML, y también realizar separaciones. De manera automática se generan estos widgets y se pueden utilizar en la aplicación. Estas características representan una ventaja en el desarrollo y por eso se tiene la intención de implementar una libreria con prestaciones similares pero para WxPython?.
Características de la librería
- Creación de barras de menús, de herramientas y menús contextuales (popups).
- Lectura del XML a partir de una cadena, o de un archivo
- Combinación de varias definiciones de XML.
- Los elementos deberían estar asociados, de modo que cambien al modificarse su definición.
- Las definiciones XML deben permitir definir los callbacks para los elementos, y debe definir una forma para autoconectar esos callbacks.
- Soporte para internacionalización.
Ideas de implementación
Entre las posibilidades para la implementación, se encuentra el uso de las librerías estándar de Python para interpretar el XML, sin embargo, en GTK+ no es necesario colocar la etiqueta raíz "ui", dado que se pueden realizar mezclas entre distintas definiciones xml. Esto causa un problema, ya que para ser analizada con la librería estándar de Python, sería necesario que el XML tenga una sola etiqueta principal. Para evitar este problema, sería necesario agregar las etiquetas <ui> y </ui> cuando sea necesario. La otra posibilidad es la implementación de un parser sencillo para XML, sin embargo, esto es mejor evitarlo.
Dado que se definen los recursos en XML y se utiliza la librería de Python para moverse por el DOM generado, la idea es utilizar el patrón visitante para recorrer esa estructura XML y construir con nodos propios la estructrua del widget de WxPython?. Para esto se debe definir una clase Node genérica, y comenzar a crear nodos que hereden de este, una clase por tipo de elemento XML. Mientras se visita el XML, se instancia un objeto respectivo al nodo xml y se ejecuta un método de este, para que a partir de la información del XML construya el widget y lo introduzca en la jerarquia.
Un UiManager por tanto, manejará una jerarquía de este tipo, por cada uno de los recursos que tenga.
La API del UIManager
class UIManager(object): def get_widget(path): """Retorna un widget encontrado en la ruta path path -- ruta al widget Retorna None si no encuentra el elemento. """ def add_ui(ui_source): """ui_source -- Cadena o archivo de donde se extraerá la descripción XML. Se puede pensar en una función separada para analizar una cadena, y otra para analizar un archivo. """ def get_ui(): """Retorna la descripción XML de toda la interfaz actualmente mezclada. """ def remove_ui(merge_id): """Realiza un unmerge de los elementos identificados por el merge_id. """ def new_merge_id(): """Genera un id único secuencial.""" def ensure_update(): """Actualiza inmediatamente los cambios realizados a la interfaz, no con un idle function.
El formato XML
<ui>
<menubar name="MainMenubar">
<menu name="&Planets"> <!-- La búsqueda debería ignorar el & -->
<menuitem name="&Mercury" id="2"/> <!-- El id es opcional en todos los elementos que son un widget -->
<menuitem name="&Venus"/>
<menuitem name="&Earth"/>
<separator/>
<menuitem name="&Close"> <!-- comment="Close this frame" -->
<comment>Close this Frame</comment>
<!-- El manejador es la función o método que se conecta al evento
La idea es que al enviar un XML al uimanager se le envie un espacio de nombres (módulo u objeto)
al cual se le ejecutará un getattr para conectar automáticamente respuestas a eventos-->
<handler event="EVT_MENU"> <!-- probablemente con un eval("self.Bind(wx.%s, %s)) -->
CloseWindow
</handler>
</menuitem>
</menu>
<menu name="&Elements"> <!-- La búsqueda debería ignorar el & -->
<menuitem name="Hidrogen"/> <!-- El id es opcional en todos los elementos que son un widget -->
<menuitem name="Helium"/>
<menuitem name="Lanthanides">
<menuitem name="Lanthanium"/>
<menuitem name="Cerium"/>
<menuitem name="Praseodimium"/>
</menuitem>
<separator/>
</menu>
<menu name="&Shells">
<!-- El tipo es por defecto, item_normal, pero los contenidos por defecto serán del tipo del contenedor a no ser
que se especifique lo contrario -->
<placeholder name="Shells" type="ITEM_RADIO"> <!-- El placeholder agrupa en una ubicación varios elementos -->
<menuitem name="Lanthanium"/>
<menuitem name="Cerium"/>
<menuitem name="Praseodimium"/>
</placeholder>
<separator/>
<menuitem name="project1"/>
<menuitem name="project2" type="ITEM_NORMAL"/> <!-- El tipo se puede especificar-->
</menu>
<menu name="Chec&k">
<placeholder name="Checks" type="ITEM_CHECK">
<menuitem name="Letters"/>
<menuitem name="Digits"/>
<menuitem name="Letters and Digits"/>
</placeholder>
</menu>
<menu name="&Fun">
<menuitem name"&Smile" shortcut="Ctrl+S">
<comment>Este tiene un icono<comment/>
<icon>smily_icon</icon> <!-- Se saca del mismo contexto que los métodos -->
</menuitem>
<menuitem name="Interesting thing" shortcut="Ctrl+A">
<comment>Note the Shortcut</comment>
</menuitem>
<separator/>
<menuitem name="Hello" shortcut="Ctrl+H"/>
<separator/>
</menu>
</menubar>
</ui>
TODO: Como implementar la internacionalización.
