Manual del desarrollador

Si usó las instrucciones de instalación de devinstall, ha descargado las fuentes conectadas al repositorio de github. Se encuentra en la situación ideal para comenzar a investigar el software, comprender cómo funciona y contribuir al desarrollo de ghini.desktop.

Ayuda al desarrollo de Ghini

Si desea contribuir al Ghini, puede hacerlo de varias diferentes maneras:

  • Utilice el software, anote lo que menos le gusta, abra una issue para cada punto. Un desarrollador se activará antes que usted pueda imaginarse.
  • Si opina que algo le hace falta al software pero no cree poderlo formalizar en cuestiones separadas, podría considerar contratar a un profesional. Se trata de la mejor manera de asegurarse de que algo ocurra rápido en Ghini. Asegúrese de que el desarrollador abra issues y publique sus contribuciones en github.
  • ¡Traducir! Cada ayuda con las traducciones será bienvenida, así que por favor ¡traduzca! puede hacer esto sin necesidad de instalar nada en tu ordenador, simplemente usando el servicio de traducción en línea ofrecido por weblate
  • haga un fork del repositorio, eliga un problema, lo resuelva, abra una pull request. Ver el bug solving workflow a continuación.

Si todavía no has instalado Ghini y quiere echar un vistazo a la historia de su código y documentación, puede abrir nuestra página de github y ver todo lo que ha estado sucediendo con Ghini, desde sus inicios en el año 2004 bajo el nombre de Bauble.

Si instaló el software según las instrucciones devinstall, ya tiene todo el historial completo en el clone git local.

Fuente del software, versiones, ramas

Si desea una versión particular de Ghini, nosotros las ponemos a disposición como ramas (branch). Usted debe git checkout de la rama correspondiente a la versión que desea.

línea de producción

Nombres de rama para las versione esbables (producción) de Ghini son de la forma ghini-x.y (por ejemplo: ghini-1.0); nombres de rama para el desarrollo de Ghini son de la forma ghini-x.y-dev (por ejemplo: ghini-1.0-dev).

Flujo de trabajo de desarrollo

Nuestro flujo de trabajo incluye la entrega rápida y continua a la rama en prueba, empujando frecuentemente a github, para que travis-ci y coveralls.io comprueben la calidad de las ramas, por último, de vez en cuando, fusionando la rama en prueba en la versión correspondiente.

Cuando trabajo en cuestiones mayores, que pienso van a durar más que un par de días, podría preferir abrir una rama asociada a la cuestión. No hago esto muy a menudo.

cuestiones mayores

Frente a un tema mayor bien identificado, crear una rama etiquetada en el final de una línea de desarrollo principal (por ejemplo: ghini-1.0-dev) y seguir el flujo de trabajo descrito en

https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging

en breve:

git up
git checkout -b issue-xxxx
git push origin issue-xxxx

Trabajar en la nueva rama temporal. Cuando esté listo, vaya a github, fusione la rama con la línea principal de desarrollo de que ramificó, resuelva los conflictos si necesario, elimine la rama temporal.

Cuando esté listo para su publicación, combinar la línea de desarrollo en la línea de producción correspondiente.

Actualizar el conjunto de cadenas traducibles

De vez en cuando, durante el proceso de actualización del software, el programador añade o modifica cadenas de texto en las fuentes python, o en la documentación, o en las fuentes glade. La mayoría de nuestras cadenas son traducibles y son automáticamente ofrecidas a weblate para la contribución de traducciones por voluntarios, en forma de archivos po.

Un archivo po está asociado a un idioma y contiene texto en pares: original y traducción. Cuando un traductor añade una traducción en weblate, eso es copiado a nuestro repositorio en github. Cuando un programador añade un texto en el software, esto es copiado a weblate para ser traducido.

Weblate alberga el proyecto Ghini. El proyecto está organizado en componentes, cada cual corresponde a una rama de uno de nuestros repositorios en github. Cada componente acepta traducciones a varios idiomas.

componente repositorio rama
Desktop 1.0 ghini.desktop ghini-1.0-dev
Desktop 3.1 ghini.desktop ghini-3.1-dev
Documentation 1.0 ghini.desktop-docs.i18n ghini-1.0-dev
Documentación de Ghini 1.0 ghini.desktop-docs.i18n ghini-3.1-dev
Web 1.2 ghini.web master
Pocket ghini.pocket master
Tour ghini.tour master

Para poner al día los archivos po relativos al software, active una linea de comando en la carpeta base del checkout de ghini.desktop y ejecute el script:

./scripts/i18n.sh

Para poner al día los archivos po relativos a la documentación, active una linea de comando en la carpeta base del checkout de ghini.desktop-docs.i18n y ejecute el script:

./doc/runme.sh

Cuando se ejecuta uno de los scripts mencionados, lo más probable es que usted necesite alterar todos archivos po del proyecto. Puedes querer revisar los cambios antes de enviarlos el repositorio. Esto es muy importante cuando se realiza una corrección marginal en una cadena, como quitar un error de tipeo.

Algo que pasa: acabar en un conflicto. Resolución de conflictos no es difícil una vez que uno sepa cómo se hace. En primer lugar, añadir weblate como repositorio remoto:

git remote add weblate-doc10 https://hosted.weblate.org/git/ghini/documentation-10/

A continuación, asegúrese de estar en el repositorio correcto, en la rama correcta, actualizar la fuente remota, fusionar con ella:

git checkout ghini-1.0-dev
git remote update
git merge weblate-doc10/ghini-1.0-dev

“Nuestra documentación <https://readthedocs.org/projects/ghini/ >`_ en readthedocs tiene una versión original en inglés y varias traducciones. Seguimos la `descripción de localización <http://docs.readthedocs.io/en/latest/localization.html >`_, no hay nada que nosotros mismos inventamos aquí.

Readthedocs comprueba la configuración de idioma del proyecto e invoca sphinx-intl para formatear la documentación en el idioma de objetivo. Con la configuración predeterminada, que no alteramos, sphinx-intl espera un archivo po por cada documento fuente, llamado como el documento de origen, y que resida en el directorio local/$(LANG)/LC_MESSAGES/.

Por otro lado, Weblate (y nosotros) preferimos un único fichero po por lenguaje, y los mantenemos todos en el mismo fichero /po, tal como lo hacemos para el proyecto de software: /po/$(LANG).po.

Con el fin de no repetir la información, y para permitir que ambos sistemas trabajen en su forma natural, tenemos dos conjuntos de enlaces simbólicos (git los honra).

Para resumir: cuando se actualiza un archivo de la documentación, el script runme.sh se ocupa de:

  1. copia los archivos rst del software a la documentación;
  2. crea un nuevo archivo pot para cada documento;
  3. junta los archivos pot en un único doc.pot;
  4. pone al día todos doc.po (uno por idioma) en base al nuevo doc.pot;
  5. crea todos enlaces simbólicos:
    1. para sphinx-int en /local/$(LANG)/LC_MESSAGES/
    2. para weblate en /po/$(LANG).po

Definitivamente podríamos escribir lo anterior en un Makefile, o incluso mejor incluirlo en doc/Makefile. Quién sabe, tal vez lo haremos.

Producing the docs locally

The above description is about how we help external sites produce our documentation so that it is online for all to see. But what if you want to have the documentation locally, for example if you want to edit and review before pushing your commits to the cloud?

In order to run sphinx locally, you need to install it within the same virtual environment as ghini, and to install it there, you need to have a sphinx version whose dependencies don not conflict with ghini.desktop’s dependecies.

What we do to keep this in order?

We state this extra dependency in the setup.py file, as an extras_require entry. Create and activate the virtual environment, then run easy_install ghini.desktop[docs]. This gets you the sphinx version as declared in the setup.py file.

If all you want is the html documentation built locally, run ./setup.py install docs. For more options, enter the doc directory and run make.

Como hacen nuestras cadenas de texto para llegar a los usuarios?

Hace poco un traductor me puso esta pregunta, añadiendo: »se trata de un proceso automático de Weblate a Git al Ghini desktop ya instalado, o requiere intervención manual?

Es que la interacción es algo más compleja, y depende de cuál componente estamos considerando.

When you install ghini.desktop or one of the Android apps, the installation doesn’t assume a specific run-time language: a user can change their language configuration any time. So what we do is to install the software in English together with a translation table from English to whatever else.

At run-time the GUI libraries (Android or GTK) know where to look for the translation strings. These translation tables are generated during the installation or upgrade process, based on the strings you see on Weblate.

The path followed by translations is: You edit strings on Weblate, Weblate keeps accumulating them until you are done, or you don’t interact with Weblate for a longer while; Weblate pushes the strings to github, directly into the development line ghini-1.0-dev; I see them and I might blindly trust or prefer to review them, maybe I look them up in wikipedia or get them translated back to Italian, Spanish or English by some automatic translation service; sometimes I need to solve conflicts arising because of changed context, not too often fortunately. As said, this lands in the development line ghini-1.0-dev, which I regularly publish to the production line ghini-1.0, and this is the moment when the new translations finally make it to the distributed software.

Users will notice a new version available warning and can decide to ignore it, or to update.

For ghini.pocket, it is similar, but the notification is handled by the Android system. We publish on the Play Store, and depending on your settings, your phone will update the software automatically, or only notify you, or do nothing. It depends on how you configured automatic updates.

For ghini.web, we haven’t yet defined how to distribute it.

Para la documentación de ghini, el proceso es totalmente automático, manejado por readthedocs.org.

Añadir pruebas unitarias

Si te interesa contribuir al desarrollo de Ghini, una buena manera de hacerlo sería por ayudarnos a encontrar y escribir las pruebas unitarias que falta.

Una función de bien probada es uno cuyo comportamiento se puede cambiar sin romper al menos una prueba de unidad.

Todos estamos de acuerdo que en teoría de la teoría y la práctica coinciden perfectamente y que uno escribe las pruebas primero implementa la función. En la práctica, sin embargo, práctica no coincide con la teoría y nos hemos estado escribiendo pruebas después de escribir y publicar incluso las funciones.

Esta sección describe el proceso de agregar pruebas unitarias para bauble.plugins.plants.family.remove_callback.

Para probar

En primer lugar, abra el índice del informe de cobertura y eliga un archivo con baja cobertura.

Para este ejemplo, en octubre de 2015, aterrizamos en bauble.plugins.plants.family, en el 33%.

https://coveralls.io/builds/3741152/source?filename=bauble%2Fplugins%2Fplants%2Ffamily.py

Las dos primeras funciones que necesitan pruebas, edit_callback y add_genera_callback, incluyen la creación y activación de un objeto depende de un cuadro de diálogo personalizado. Debe realmente primeras pruebas de unidad de escritura para esa clase y luego volver aquí.

La siguiente función, remove_callback, también activa un par de cuadros de diálogo y mensajes, pero en la forma de invocar una función de solicitud de entrada del usuario mediante cajas de sí-no-ok. Estas funciones podemos reemplazar fácilmente con una función de burlarse de la conducta.

Cómo se hace

Así, después de haber decidido qué describir en prueba de la unidad, nos fijamos en el código y vemos que es necesario discriminar un par de casos:

** parámetro corrección **
  • la lista de familias no tiene elementos.
  • la lista de familias tiene más de un elemento.
  • la lista de las familias tiene exactamente un elemento.
cascade
  • la familia no tiene géneros
  • la familia tiene uno o más géneros
confirmar
  • el usuario confirma la eliminación
  • el usuario no confirma la eliminación
borrar
  • todo va bien al eliminar la familia
  • hay un error al eliminar la familia

Decidir sólo se centrará en la ** cascada ** y el ** confirmar ** aspectos. Dos preguntas binarias: 4 casos.

donde poner las pruebas

Localizar el script de prueba y elija la clase donde poner las pruebas de unidad extra.

https://coveralls.io/builds/3741152/source?filename=bauble%2Fplugins%2Fplants%2Ftest.py#L273

que se hace con las pruebas ignoradas

La clase de FamilyTests contiene una prueba omitida, implementarlo a ser un poco de trabajo porque necesitamos reescribir la FamilyEditorPresenter, la separan de la FamilyEditorView y reconsiderar qué hacer con la clase FamilyEditor, que creo que debe eliminado y reemplazado con una sola función.

las pruebas de la escritura

Después de la última prueba en la clase de FamilyTests, añadir los cuatro casos que quiero describir, y aseguro que no, y como soy perezosa, escribo el código más compacto que conozco para generar un error:

def test_remove_callback_no_genera_no_confirm(self):
    1/0

def test_remove_callback_no_genera_confirm(self):
    1/0

def test_remove_callback_with_genera_no_confirm(self):
    1/0

def test_remove_callback_with_genera_confirm(self):
    1/0

Una prueba, paso a paso

Vamos a empezar con el primer caso de prueba.

Al escribir ensayos, generalmente siguen el patrón:

  • T₀ (condición inicial),
  • acción,
  • T₁ (prueba el resultado de la acción dada las condiciones iniciales)

¿qué hay en un nombre — las pruebas unitarias

Hay una razón por la cual las pruebas unitarias se llaman pruebas unitarias. Por favor, nunca pruebe dos acciones en una prueba.

Así que vamos a describir T₀ de la primera prueba, una base de datos manteniendo una familia sin géneros:

def test_remove_callback_no_genera_no_confirm(self):
    f5 = Family(family=u'Arecaceae')
    self.session.add(f5)
    self.session.flush()

No queremos que la función está probadaspara para invocar la función interactiva utils.yes_no_dialog, queremos remove_callback para invocar una función de reemplazo no interactivo. Ello simplemente haciendo punto de utils.yes_no_dialog a una expresión de lambda que, al igual que la original función interactiva, acepta un parámetro y devuelve un valor booleano. En este caso: False:

def test_remove_callback_no_genera_no_confirm(self):
    # T_0
    f5 = Family(family=u'Arecaceae')
    self.session.add(f5)
    self.session.flush()

    # action
    utils.yes_no_dialog = lambda x: False
    from bauble.plugins.plants.family import remove_callback
    remove_callback(f5)

A continuación probamos el resultado.

Bueno, no sólo queremos prueba si o no el objeto Arecaceae fue suprimida, nosotros también debemos el valor devuelto por remove_callback, y si yes_no_dialog y message_details_dialog se invocaron o no.

Una expresión de lambda no es suficiente para esto. Hacemos algo aparentemente más complejo, que hará la vida mucho más fácil.

Primero definamos una función más bien genérica:

def mockfunc(msg=None, name=None, caller=None, result=None):
    caller.invoked.append((name, msg))
    return result

y parcial del functools módulo estándar, parcialmente aplicar arriba mockfunc, dejando sólo mensaje no se especifica, y utilizar esta aplicación parcial, que es una función que acepta un parámetro y devuelve un valor, para reemplazar las dos funciones en utils. La función de prueba ahora se ve así:

def test_remove_callback_no_genera_no_confirm(self):
    # T_0
    f5 = Family(family=u'Arecaceae')
    self.session.add(f5)
    self.session.flush()
    self.invoked = []

    # action
    utils.yes_no_dialog = partial(
        mockfunc, name='yes_no_dialog', caller=self, result=False)
    utils.message_details_dialog = partial(
        mockfunc, name='message_details_dialog', caller=self)
    from bauble.plugins.plants.family import remove_callback
    result = remove_callback([f5])
    self.session.flush()

La sección de prueba comprueba que no se invocó el message_details_dialog, que yes_no_dialog fue invocado, con el parámetro de mensaje correcto, que existe todavía la Arecaceae:

# effect
self.assertFalse('message_details_dialog' in
                 [f for (f, m) in self.invoked])
self.assertTrue(('yes_no_dialog', u'Are you sure you want to '
                 'remove the family <i>Arecaceae</i>?')
                in self.invoked)
self.assertEquals(result, None)
q = self.session.query(Family).filter_by(family=u"Arecaceae")
matching = q.all()
self.assertEquals(matching, [f5])

Etcétera

«hay dos clases de personas, aquellos que terminan lo que empiezan y así sucesivamente»

Próxima prueba es casi lo mismo, con la diferencia que el utils.yes_no_dialog debe volver True (esto logramos especificando resultado = True en la aplicación parcial de la genérico mockfunc).

Con esta acción, el valor devuelto por remove_callback debe ser True, y no debe haber ninguna familia Arecaceae en la base de datos más:

def test_remove_callback_no_genera_confirm(self):
    # T_0
    f5 = Family(family=u'Arecaceae')
    self.session.add(f5)
    self.session.flush()
    self.invoked = []

    # action
    utils.yes_no_dialog = partial(
        mockfunc, name='yes_no_dialog', caller=self, result=True)
    utils.message_details_dialog = partial(
        mockfunc, name='message_details_dialog', caller=self)
    from bauble.plugins.plants.family import remove_callback
    result = remove_callback([f5])
    self.session.flush()

    # effect
    self.assertFalse('message_details_dialog' in
                     [f for (f, m) in self.invoked])
    self.assertTrue(('yes_no_dialog', u'Are you sure you want to '
                     'remove the family <i>Arecaceae</i>?')
                    in self.invoked)
    self.assertEquals(result, True)
    q = self.session.query(Family).filter_by(family=u"Arecaceae")
    matching = q.all()
    self.assertEquals(matching, [])

Echa un vistazo en 734f5bb9feffc2f4bd22578fcee1802c8682ca83 commit para las otras dos funciones de la prueba.

Pruebas y Registro

Nuestros objetos de bauble.test.BaubleTestCase utilizan handler de la clase bauble.test.MockLoggingHandler. Cada vez que se inicia una prueba unitaria, el método setUp crea un nuevo handler y lo asocia al logger root. El método de teardDown se encarga de eliminarlo.

Se puede comprobar la presencia de mensajes de registro específicos en self.handler.messages. mensajes es un diccionario, inicialmente vacío, con dos niveles de indexación. Primero el nombre del registrador añadiendo al registro, luego el nombre del nivel del mensaje de registro. Las claves son producidas al necesitarse. Los valores en el diccionario son listas de mensajes, con formato según el formateador asociado al controlador, por defecto logging.Formatter("%(message)s").

Al necesitarlo, puede borrar los mensajes colectados mediante la invocación de self.handler.clear().

Al ponerlo todo junto

De vez en cuando usted quiere activar la clase de prueba trabaja en:

nosetests bauble/plugins/plants/test.py:FamilyTests

Y al final del proceso que desea actualizar las estadísticas de:

./scripts/update-coverage.sh

Estructura de interfaz de usuario

La interfaz de usuario se construye según el ** modelo ** — ** vista - presentador ** patrón arquitectónico. Gran parte de la interfaz, ** modelo ** es un objeto de base de datos de SQLAlchemy, pero también tenemos elementos de la interfaz donde existe un modelo de base de datos correspondiente. En general:

  • El ** ver ** se describe como parte de un ** claro ** archivo. Esto debe incluir la señal de llamada y ListStore TreeView asociaciones. A reutilizar la clase base que define “” GenericEditorView”” en “” bauble.editor””. Cuando se crea una instancia de esta clase genérica, pasarlo el ** claro ** nombre del archivo y el nombre del widget de raíz, luego entregar esta instancia a la ** presentador ** constructor.

    En el archivo glade, en la sección de action-widgets que cierra la descripción del objeto GtkDialog, asegúrese de que cada elemento action-widget tenga un valor response válido Use los valores válidos de GtkResponseType, por ejemplo:

    • GTK_RESPONSE_OK, -5
    • GTK_RESPONSE_CANCEL, -6
    • GTK_RESPONSE_YES, -8
    • GTK_RESPONSE_NO, -9

    No hay manera fácil de escribir pruebas unitarias para una subclase de una vista, así que por favor no subclasen vistas, no hay realmente ninguna necesidad.

    En el archivo de glade, cada widget de entrada debe definir qué controlador se activa en que señal. La clase genérica de presentador ofrece llamadas genéricas que cubren los casos más comunes.

    • GtkEntry (entrada de texto de línea singola) encargará de la señal de changed, con on_text_entry_changed o on_unique_text_entry_changed.
    • GtkTextView: asociarlo a un GtkTextBuffer. Para manejar la señal changed en la GtkTextBuffer, tenemos que definir un manejador que invoca el genérico on_textbuffer_changed, el único papel para esta función consiste en pasar a nuestro controlador genérico el nombre del atributo modelo que recibe el cambio. Este es un workaroud para un bug sin resolver en GTK.
    • GtkComboBox con textos traducidos no se pueden gestionar fácilmente desde el archivo de glade, por lo que aún no tratamos. Utilice el método de “” init_translatable_combo”” de la clase genérica de “” GenericEditorView””, pero por favor invocarlo desde el ** presentador **.
  • El modelo es sólo un objeto con atributos. En esta interacción, el modelo es un contenedor de datos pasivo, no hace nada más sino dejar que el presentador los modifique.

  • Lo que fue obtenido como subclase de presentador define e implementa:

    • widget_to_field_map, un diccionario que asocia nombres de widget a nombre de los atributos del modelo,
    • view_accept_buttons, la lista de nombres de widget que, si activados por el usuario, significa que la vista debe ser cerrada,
    • todos los callbacks necesarios,
    • Opcionalmente, también juega el papel de modelo.

    El presentador actualiza continuamente el modelo según los cambios en la vista. Si el modelo corresponde a un objeto de base de datos, el presentador entrega todas actualizaciones en el modelo a la base de datos cuando la vista se cierra con éxito, o los deshace si la vista se cancela. (este comportamiento es influenciado por el parámetro do_commit)

    Si el ** modelo ** es otra cosa, entonces el ** presentador ** hará algo más.

    Nota

    Un buen comportamiento ** presentador ** utiliza la ** vista ** api para consultar los valores insertados por el usuario o por la fuerza a establecer Estados de widget. Por favor, no se aprende de la práctica de nuestros presentadores portando mal, algunos que manejar directamente campos de view.widgets. Al hacerlo, estos presentadores nos impide escribir pruebas unitarias.

La clase base para el presentador, GenericEditorPresenter en bauble.editor, implementa muchas devoluciones de llamada genéricos útiles. Hay una clase de MockView, que se puede utilizar al escribir pruebas para sus presentadores.

Ejemplos

Contact y ContactPresenter se implementan siguiendo las líneas de arriba. La vista se define en el archivo contact.glade.

Un buen ejemplo de patrón de vista/presentador (no modelo) está dada por el administrador de conexiones.

Se utiliza el mismo patrón arquitectónico de interacción no-base de datos, estableciendo el presentador también como modelo. Para ello, por ejemplo, el cuadro de diálogo de exportación JSON. El siguiente comando le dará una lista de instancias de GenericEditorView:

grep -nHr -e GenericEditorView\( bauble

Ghini extensible con Plugins

Casi todo en Ghini es extensible mediante plugins. Un Plugin puede crear tablas, definir búsquedas personalizadas, agregar elementos de menú, crear comandos personalizados y mucho más.

Para crear un nuevo plugin debe extender la clase de bauble.pluginmgr.Plugin.

La Tag es un ejemplo mínimo, incluso si el TagItemGUI cae fuera el patrón de arquitectura Model-View-Presenter.

Estructura de plugins

Ghini es un marco para el manejo de colecciones y se distribuye junto con un conjunto de plugins haciendo Ghini un gerente de colección botánica. Pero Ghini mantiene un marco y podría en teoría quitar todos los plugins que distribuir y escribir su propio, o escribir tus propios plugins que ampliar o completar el actual comportamiento de Ghini.

Una vez que ha seleccionado y abre una conexión de base de datos, aterrizas en la ventana de búsqueda. La ventana de búsqueda es una interacción entre dos objetos: SearchPresenter (SP) y vista búsqueda (SV).

SV es lo que ves, SP contiene el estado del programa y encarga de las peticiones que expresan a través de SV. Manejo de estas solicitudes afectan el contenido del SV y la situación del programa en SP.

Resultados de la búsqueda se muestra en la parte más grande de SV son filas, objetos que son instancias de clases registradas en un plugin.

Cada una de estas clases debe implementar una cantidad de funciones para comportarse adecuadamente en el marco de Ghini. El marco de Ghini Reserva espacio para clases de acoplamiento.

SP sabe de todas las clases (enchufadas) registradas, que son almacenados en un diccionario, asociar una clase a su implementación del plugin. SV tiene una ranura (un gtk. De la caja) donde usted puede agregar elementos. En cualquier momento, a lo sumo sólo un elemento en la ranura es visible.

Un plugin define una o más clases de plugin. Una clase de plugin desempeña el papel de presentador parcial (pP - plugin presentador) implementar las devoluciones de llamadas necesitadas el vista parcial asociada en la ranura (pV - plugin view), y el patrón MVP es completado por el presentador de padre (SP), otra vez actuando como modelo. Resumir y completar:

  • SP actúa como modelo,
  • la vista parcial de pV se define en un archivo de glade.
  • las devoluciones de llamadas implementadas por pP se hace referencia en el archivo de glade.
  • un menú contextual para la fila de SP,
  • una característica de los niños.

Cuando se registra una clase de plugin, el SP:

  • agrega el pV en la ranura y hace no visible.
  • agrega una instancia del pP en las clases de plugin registrados.
  • dice el pP que el SP es el modelo.
  • conecta todas las devoluciones de llamada de pV pP.

Cuando un elemento en pV desencadena una acción en el pP, el pP podrá enviar la acción a SP y puede solicitar SP que actualiza el modelo y actualiza la vista.

Cuando el usuario selecciona una fila en SP, SP oculta todo en la ranura de acoplamiento y muestra sólo el pV solo en relación con el tipo de la fila seleccionada y pide al pP para refrescar el pV con lo que es relativo a la fila seleccionada.

Aparte de establecer la visibilidad de la pV varios, nada necesita discapacitado ni quitado: un pV invisible no puede desencadenar eventos!

error solución de flujo de trabajo

flujo de trabajo de desarrollo normal

  • si al usar el software, usted nota un problema, o se le occurre de como mejorar algo, pienselo lo suficiente para perfeccionar su idea de lo que realmente es, lo que ha notado. Abra un tema y nos comparta su idea. Alguien podría reaccionar con notas.
  • puede visitar el sitio de temas y eligir uno que desea abordar.
  • asigne el tema a si mismo, de esta manera informa al mundo que tiene la intención de trabajar en ello. alguien podría reaccionar con notas.
  • Opcionalmente el repositorio en su cuenta de la bifurcación y preferiblemente crear una rama, claramente asociada a la cuestión.
  • escribir pruebas unitarias y consignar a su rama (preferiblemente, no empuje a github pruebas unitarias fallando, ejecute nosetests localmente primero).
  • escribir mas pruebas unitarias (idealmente, las pruebas forman la descripción completa de la función que está agregando o corrigiendo).
  • Asegúrese de que la función que se agrega o corrige es realmente completamente descrita por las pruebas unitarias que usted escribió.
  • Asegúrese de que las pruebas de unidad son atómicas, es decir, que cada una controle efectos de cambios relativos a una sola variable. no le dé a pruebas unitarias valores de entrada complejos o pruebas que no caben en una sola pantalla (25 líneas de código).
  • Escriba el código que hace que las pruebas tengan éxito.
  • actualizar los archivos de i18n (ejecutar “”./scripts/i18n.sh””).
  • siempre que sea posible, traducir las cadenas nuevo que pones en archivos de código o glade.
  • cuando modifique cadenas, por favor, asegúrese de que las traducciones antiguas siguen aplicandose.
  • cometer los cambios.
  • empuje a github.
  • abrir una solicitud de extracción.

publicar en la linea de producción

please use the publish.sh script, in the scritps directory. This one takes care of every single step, and produces recognizable commit comments, it publishes the release on pypi, and in perspective it will contain all steps for producing a deb file, and a windows executable.

you can also do this by hand:

  • abrir el tirón solicitud página usando como base una cadena de producción “” ghini-x.y””, en comparación con “” ghini-x.y-dev””.
  • Asegúrese de que confirmar “” bache “” está incluido en las diferencias.
  • debe ser posible fusionar automáticamente las ramas.
  • crear la nueva solicitud de extracción, lo llaman como «publicar en la línea de producción».
  • posiblemente necesita esperar para que travis-ci realizar los controles.
  • combinar los cambios.

don’t forget to tell the world about the new release: on facebook, the google group, in any relevant linkedin group, and on our web page.

su propio “fork”

Si desea mantener su propio fork del proyecto, tenga en cuenta que esto está en continuo progreso, por lo que mantenerse actualizado requerirá algún esfuerzo de su parte.

La mejor manera de mantener el propio “fork” consiste en enfocarse en algún tema específico, trabajar relativamente rápido, a menudo abrir solicitudes de “pull” para su trabajo, y asegurarse que sea aceptado. Sólo siga el estilo de programación de Ghini, añada pruebas unitarias, concisas y abundantes, y no habrá ningún problema en ver su trabajo incluido en Ghini.

Si su fork se puso fuera de sincronización con Ghini de upstream: leer, entender, seguir el guía de github sobre configurar los remotos y como sincronizar.

paso final

  • revisar el flujo de trabajo. considerar esto como una guía, a ti y a tus compañeros. por favor, ayudar a hacer mejor y coincidencia de la práctica.

Distributing ghini.desktop

Python Package Index - PyPI

This is not much mentioned, but we keep ghini.desktop on the Python Package Index, so you could install it by no more than:

pip install ghini.desktop

There are a couple packages that can’t be installed with pip, but otherwise that’s really all you need to type, and it’s platform independent.

Publishing on PyPI is a standard setup command:

python setup.py sdist --formats zip upload -r pypi

Windows

For building a Windows installer or executable you need a running Windows system. The methods described here has been used successfully on Windows 7, 8 and 10. Windows Vista should also work but has not been tested.

If you are on GNU/Linux, or on OSX, you are not interested in the remainder of this section. None of Ghini’s contributors knows how to produce a Windows installer without having a Windows system.

The goal of the present instructions is to help you produce a Windows installer, that is a single executable that you can run on any Windows workstation and that will install a specific version of ghini.desktop. This is achieved with the NSIS script-driven installer authoring tool.

As a side product of the installer production, you will have a massive but relocatable directory, which you can copy to a USB drive and which will let you use the software without needing an installation.

The files and directories relevant to this section:

  • scripts/build-win.bat — the single batch script to run.
  • setup.py — implements the NSIS and py2exe commands.
  • scripts/build-multiuser.nsi — the nsis script, used by the above.
  • nsis/ — contains redistributable NSIS files, put here for conveniency.
  • ghini-runtime/ — built by py2exe, used by nsis.
  • dist/ — receives the executable installation file.

Most steps are automated in the build-win.bat script. Installation of a few tools needs to be done manually:

  1. Download and install Git, Python 2.7 and PyGTK.

    This is outlined in the devinstall-based installation instructions.

  2. Download and install NSIS v3.

  3. A reboot is recommended.

  4. Clone the ghini.desktop repository.

    Use your own fork if you plan contributing patches, or the organization’s repository https://github.com/Ghini/ghini.desktop.git if you only wish to follow development.

    Clone the repository from GitHub to wherever you want to keep it, and checkout a branch. Replace <path-to-keep-ghini> with the path of your choice, e.g. Local\github\Ghini\. Production branch ghini-1.0 is recommended as used in the example.

    To do this, open a command prompt and type these commands:

    cd <path-to-keep-ghini>
    git clone <ghini.desktop repository URL>
    cd ghini.desktop
    git checkout ghini-1.0
    

The result of the above is a complete development environment, on Windows, with NSIS. Use it to follow development, or to propose your pull requests, and to build Windows installers.

All subsequent steps are automated in the scripts\build_win.bat script. Run it, and after a couple of minutes you should have a new dist\ghini.desktop-<version>-setup.exe file, and a working, complete relocatable directory named ghini-runtime.

Read the rest if you need details about the way the script works.

The build_win.bat script

A batch file is available that can complete the last few steps. To use it use this command:

scripts\build_win.bat

build_win.bat accepts 2 arguments:

  1. /e — executable only.

    Produce an executable only, skipping the extra step of building an installer, and will copy win_gtk.bat into place.

  2. venv_path — A path to the location for the virtual environment to use.

    Defaults to "%HOMEDRIVE%%HOMEPATH%"\.virtualenvs\%CHECKOUT%-exe, where CHECKOUT corresponds to the name of the branch you checked out.

If you want to produce an executable only and use a virtual environment in a folder beside where you have ghini.desktop, you could execute scripts\build_win.bat /e ..\ghi2exe

py2exe no funciona con eggs

Para producir un ejecutable Windows no debes instalar los paquetes como eggs. Hay varios métodos para alcanzarlo, por ejemplo:

  • Install using pip. The easiest method is to install into a virtual environment that doesn’t currently have any modules installed as eggs using pip install . as described below. If you do wish to install over the top of an install with eggs (e.g. the environment created by devinstall.bat) you can try pip install -I . but your mileage may vary.

  • By adding:

    [easy_install]
    zip_ok = False
    

    to setup.cfg (or similarly zip_safe = False to setuptools.setup() in setup.py) you can use python setup.py install but you will need to download and install Microsoft Visual C++ Compiler for Python 2.7 to get any of the C extensions and will need a fresh virtual environment with no dependent packages installed as eggs.

The included build-win script uses the pip method.

installing virtualenv and working with environments

Install virtualenv, create a virtual environment and activate it.

With only Python 2.7 on your system (where <path-to-venv> is the path to where you wish to keep the virtual environment) use:

pip install virtualenv
virtualenv --system-site-packages <path-to-venv>
call <path-to-venv>\Scripts\activate.bat

On systems where Python 3 is also installed you may need to either call pip and virtualenv with absolute paths, e.g. C:\Python27\Scripts\pip or use the Python launcher e.g. py -2.7 -m pip (run python --version first to check. If you get anything other than version 2.7 you’ll need to use one of these methods.)

Populate the virtual environment

Install dependencies and ghini.desktop into the virtual environment:

pip install psycopg2 Pygments py2exe_py2
pip install .

Compile for Windows

Build the executable:

python setup.py py2exe

The ghini-runtime folder will now contain a full working copy of the software in a frozen, self contained state.

This folder is what is packaged by NSIS.

This same folder can also be transferred however you like and will work in place. (e.g. placed on a USB flash drive for demonstration purposes or copied manually to C:\Program Files with a shortcut created on the desktop). To start ghini.desktop double click ghini.exe in explorer (or create a shortcut to it).

Fixing paths to GTK components.

If you run the relocatable compiled program, unpackaged, you might occasionally have trouble with the GUI not displaying correctly.

Should this happen, you need to set up paths to the GTK components correctly. You can do this by running the win_gtk.bat, from the ghini-runtime folder.

You will only need to run this once each time the location of the folder changes. Thereafter ghini.exe will run as expected.

Finally, invoke NSIS

Build the installer:

python setup.py nsis

This should leave a file named ghini.desktop-<version>-setup.exe in the dist folder. This is your Windows installer.

about the installer

  • Capable of single user or global installs.

  • At this point in time ghini.desktop installed this way will not check or or notify you of any updated version. You will need to check yourself.

  • Capable of downloading and installing optional extra components:

    • Apache FOP - If you want to use xslt report templates install FOP. FOP requires Java Runtime. If you do not currently have it installed the installer will let you know and offer to open the Oracle web site for you to download and install it from.
    • MS Visual C runtime - You most likely don’t need this but if you have any trouble getting ghini.desktop to run try installing the MS Visual C runtime (e.g. rerun the installer and select this component only).
  • Can be run silently from the commandline (e.g. for remote deployment) with the following arguments:

    • /S for silent;

    • /AllUser (when run as administrator) or /CurrentUser

    • /C=[gFC] to specify components where:

      g = Deselect the main ghini.desktop component (useful for adding optional component after an initial install)

      F = select Apache FOP

      C = select MS Visual C runtime

Debian

Between 2009 and 2010 someone packaged the then already obsolete Bauble 0.9.7 for Debian, and the package was included in Ubuntu. That version is still being distributed, regardless being it impossible to install.

Only recently has Mario Frasca produced a new bauble debian package, for the latest bauble.classic version 1.0.56, and proposed for inclusion in Debian. View it on mentors. This version depends on fibra, a package that was never added to Debian and which Mario also has packaged and proposed for inclusion in Debian. Mario has been trying to activate some Debian Developer, to take action. There’s not much more we can do, other than wait for a sponsor, and hoping the package will eventually get all the way to Ubuntu.

Once we get in contact with a Debian Sponsor who will review what we publish on mentors, then we will be definitely expected to keep updating the debian package for ghini.desktop and fibra.

I am not going to explain in a few words the content of several books on Debian packaging. Please choose your sources. For a very compact idea of what you’re expected to do, have a look at scripts/pubish.sh.