Manuale dello sviluppatore

If you ran the devinstall installation instructions, you have downloaded the sources, connected to the github repository. You are in the ideal situation to start looking into the software, understand how it works, contribute to ghini.desktop’s development.

Sostieni lo sviluppo di Ghini

Se volete contribuire a Ghini, può farlo in diversi modi:

  • Utilizzare il software, notare cosa piace di meno e potrebbe essere corretto, ``segnalarlo come problema <http://github.com/ghini/ghini.desktop/issues/new>`_ separatamente. Uno sviluppatore reagirà prima di quanto si possa immaginare.
  • Se avete un’idea di ciò che manca nel software ma non abbastanza e formalizzare in questioni separate, si potrebbe prendere in considerazione l’assunzione di un professionista. Questo è il modo migliore per assicurarsi che qualcosa accade rapidamente su Ghini. Assicurarsi che lo sviluppatore apra problemi su github e vi pubblichi il contributi.
  • Traduci! Qualsiasi aiuto con traduzione sarà gradito, così si prega di tradurre! è possibile farlo senza installare nulla sul tuo computer, usando solo il servizio di traduzione on-line offerti da http://hosted.weblate.org/
  • “Fork the repository”, scegliere un problema, risolverlo, aprire una richiesta “pull request”. Vedere il flusso di lavoro per risolvere bug qui sotto.

Se non hai ancora installato Ghini e desideri dare un’occhiata alla storia del suo codice, è possibile aprire la nostra pagina progetto github e vedere tutto ciò che s’è fatto intorno a Ghini fin dalla sua nascita nel 2004 come Bauble.

If you install the software according to the devinstall instructions, you have the whole history in your local git clone.

Fonte del software, versioni, rami

Se si desidera che una particolare versione di Ghini, abbiamo rilasciare e gestire versioni come rami. Si dovrebbe effettuare il git checkout del ramo corrispondente alla versione di vostra scelta.

linea di produzione

Nomi dei rami per le versioni stabili (produzione) di Ghini sono nella forma ghini-x.y (ad esempio: ghini-1.0); nomi dei rami dove sono pubblicate le versioni di prova di Ghini sono nella forma ghini-x.y-dev (ad esempio: ghini-1.0-dev).

Flusso di sviluppo

Il nostro flusso di lavoro è di impegnarsi continuamente per la branca di test, spesso di spingerli a github, di lasciare travis-ci e coveralls.io di controllare la qualità dei rami test spinti, infine, di volta in volta, per unire il ramo testing la versione corrispondente.

Quando si lavora a problemi più grandi, che sembrano richiedere più di un paio di giorni, io potrei aprire una filiale connessa alla questione. Non lo faccio molto spesso.

problemi più importanti

Quando affronta un problema più grande singolo, creare un tag di ramo all’estremità di una linea di sviluppo principale (ad es.: ghini-1.0-dev) e seguire il flusso di lavoro descritto al

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

In breve:

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

Lavorare sul nuovo ramo temporaneo. Quando è pronto, andare a github, unire il ramo con la linea di sviluppo principale da cui diramano, risolvere i conflitti, ove necessario, eliminare il ramo temporaneo.

Quando si è pronti per la pubblicazione, è possibile unire la linea di sviluppo nella linea di produzione corrispondente.

Aggiornando l’insieme di stringhe traducibili

Di tanto in tanto, durante il processo di aggiornamento del software, si verrà essere aggiungendo o modificando le stringhe nelle fonti python, nella documentazione, nelle fonti glade. La maggior parte delle nostre corde sono traducibile e sono offerti a weblate per le persone a contribuire, sotto forma di diversi file. po.

Un file po è specifico di una lingua ed è composto da coppie di testi, in lingua originale e la corrispondente traduzione. Quando un traduttore aggiunge una traduzione su weblate, questa raggiunge il nostro repositorio github. Quanto un programmatore aggiunge una stringa nel software, questa raggiunge weblate per venir tradotta.

Weblate ospita il progetto Ghini. Il progetto è suddiviso in componenti, ciascuna corrispondente ad un branch di un nostro repositorio github. Ciascuna componente accetta traduzioni in diverse lingue.

componente repositorio branch
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
Documentation 3.1 ghini.desktop-docs.i18n ghini-3.1-dev
Web 1.2 ghini.web master
Pocket ghini.pocket master
Tour ghini.tour master

Per aggiornare i file po relativi al software, esegui il seguente script dalla linea di comando, preferibilmente dalla base del checkout di ghini.desktop:

./scripts/i18n.sh

Per aggiornare i file po relativi alla documentazione, esegui il seguente script dalla linea di comando, preferibilmente dalla base del checkout di ghini.desktop-docs.i18n:

./doc/runme.sh

Quando si esegue uno degli script di cui sopra, è probabile che si abbia bisogno di fare il commit di tutti i file po del progetto. Si potrebbero voler esaminare le modifiche prima di inserirle nel respository. Questo è più importante quando si esegue una correzione marginale di una stringa, come la rimozione di un errore di battitura.

Qualcosa che accade: trovarsi con un conflitto. Risoluzione dei conflitti non è difficile se sai come si fa. Prima di tutto, aggiungere weblate come remoto:

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

Quindi assicurarsi che siamo nel repository corretto, sul ramo corretto, aggiornare la fonte remota, unirla al ramo:

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

La nostra documentazione su readthedocs ha una versione originale in lingua inglese, e diverse traduzioni. Seguiamo semplicemente la descrizione della localizzazione. Qui non c’è nulla che abbiamo inventato noi.

ReadTheDocs controlla l’impostazione «Language» del progetto ed invoca sphinx-intl per produrre la documentazione formattata nella lingua obiettivo. Con la configurazione predefinita — che non abbiamo alterato — sphinx-intl prevede un file po per ogni documento di origine, che abbia lo stesso nome del documento di origine, e che risieda nella directory locale/$(LANG)/LC_MESSAGES/.

D’altra parte, Weblate (e noi stessi) preferiamo un file po per ogni lingua, e tenerli tutti nella stessa cartella /po, come facciamo per il progetto software: /po/$(LANG).po.

Per non ripetere informazione e per lasciare lavorare entrambi i sistemi nel loro modo naturale, abbiamo due insiemi di link simbolici (git li rispetta).

Per riassumere: quando viene aggiornato un file nella documentazione, lo script``runme.sh`` si occupa di:

  1. copiare i file rst dal software alla documentazione;
  2. creare un nuovo file pot per ciascun elemento di documentazione;
  3. unire tutti i file pot in un unico doc.pot;
  4. aggiornare tutti i doc.po (uno per lingua) sulla base del nuovo doc.pot;
  5. creare tutti i collegamenti simbolici:
    1. quelli utilizzati da sphinx-intl in /local/$(LANG)/LC_MESSAGES/
    2. quelli utilizzati da noi e weblate in /po/$(LANG).po

Sicuramente potremmo scrivere tutto ciò in un Makefile, o ancora meglio includerlo nel “/ doc/Makefile “. Chissà, forse lo faremo, un giorno.

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.

Which way do the translated strings reach our users?

A new translator asked the question, adding: »Is this an automated process from Weblate –> GIT –> Ghini Desktop installed on users computers, or does this require manual steps?

The aswer is that the whole interaction is quite complex, and it depends on the component.

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.

For ghini’s documentation, it’s completely automatic, and all is handled by readthedocs.org.

Aggiunta mancante unit test

Se siete interessati a contribuire allo sviluppo di Ghini, un buon modo per farlo sarebbe aiutandoci a trovare e scrittura di unit test mancante.

Una funzione ben testata è uno cui comportamento non è possibile modificare senza rompere almeno un test di unità.

Siamo tutti d’accordo che in teoria teoria e pratica corrispondono perfettamente e che uno prima scrive i test, quindi implementa la funzione. In pratica, tuttavia, pratica non corrisponde a teoria e noi abbiamo scritto test dopo scrivendo e pubblicando anche le funzioni.

In questa sezione viene descritto il processo di aggiunta di unit test per bauble.plugins.plants.family.remove_callback.

Elementi di cui eseguire il test

Prima di tutto, aprire l’indice del rapporto di copertura e scegliere un file con bassa copertura.

In questo esempio, eseguire in ottobre 2015, siamo sbarcati su bauble.plugins.plants.family, al 33%.

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

Le prime due funzioni che hanno bisogno di prove, di edit_callback e add_genera_callback, includono la creazione e l’attivazione di un oggetto basandosi su una finestra di dialogo personalizzata. Ci dovrebbe davvero prima scrivere unit test per tale classe, poi torna qui.

La funzione successiva, remove_callback, attiva anche un paio di finestre di dialogo e il messaggio, ma in forma di richiamo di una funzione che richiede l’input dell’utente tramite caselle di sì-no-ok. Queste funzioni possiamo facilmente sostituire con una funzione beffardo il comportamento.

Come provare

Così, dopo aver deciso cosa descrivere nello unit test, guardiamo il codice e vediamo che ha bisogno di discriminare un paio di casi:

correttezza dei parametri
  • l’elenco delle famiglie non ha elementi.
  • l’elenco delle famiglie ha più di un elemento.
  • l’elenco delle famiglie ha esattamente un elemento.
cascade
  • la famiglia non ha nessun generi
  • la famiglia ha uno o più generi
confirm
  • l’utente conferma eliminazione
  • l’utente non confermare l’eliminazione
deleting
  • tutto va bene quando si elimina la famiglia
  • C’è qualche errore durante l’eliminazione della famiglia

Decido che mi concentrerò solo sugli aspetti cascata e confermare. Due domande binarie: 4 casi.

dove mettere i test

Individuare lo script di test e scegliere la classe dove mettere i test di unità extra.

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

che fare con i test disattivati

La classe FamilyTests contiene un test saltato, attuarlo sarà essere un po” di lavoro perché abbiamo bisogno di riscrivere il FamilyEditorPresenter, separarla dalla FamilyEditorView e riconsiderare ciò che a che fare con la classe FamilyEditor, che credo dovrebbe essere rimosso e sostituito con una singola funzione.

le prove di scrittura

Dopo l’ultimo test nella classe FamilyTests, aggiungere i quattro casi che voglio descrivere, e mi assicuro che falliscono, e dato che io sono pigro, scrivo il codice più compatto so per la generazione di un errore:

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 prova, passo dopo passo

Cominciamo con il primo test case.

Durante la scrittura di test, seguono in genere il modello:

  • T₀ (condizione iniziale),
  • azione,
  • T₁ (test il risultato dell’azione data le condizioni iniziali)

la ragione di un nome — test unitari

C’è una ragione perché unit test sono chiamati unit test. Si prega di test mai due azioni in un test.

Quindi cerchiamo di descrivere T₀ per il primo test, un database che tiene una famiglia senza generi:

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

Non vogliamo che la funzione in fase di test per richiamare la funzione interattiva utils.yes_no_dialog, remove_callback vogliamo di richiamare una funzione di sostituzione non interattiva. Raggiungiamo questo obiettivo semplicemente facendo punto di utils.yes_no_dialog in un’espressione lambda che, come l’originale funzione interattiva, accetta un parametro e restituisce un valore booleano. In questo 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)

Successivamente abbiamo il risultato del test.

Beh, non vogliamo solo testare o meno l’oggetto è stato eliminato Arecaceae, ci dovremmo prova anche il valore restituito da “remove_callback “, e se yes_no_dialog e message_details_dialog sono stati richiamati o non.

Un’espressione lambda non è sufficiente per questo. Facciamo qualcosa di apparentemente più complesso, che renderà la vita molto più facile.

Innanzitutto definiamo una funzione piuttosto generica:

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

e possiamo importare partial dal modulo standard functools, applicarlo parzialmente alla mockfunc, lasciando solo msg non vincolato, ed applicare ed utilizzare questa applicazione parziale, che è una funzione che accetta un parametro e restituisce un valore, per sostituire le due funzioni in utils. La funzione di test ora assomiglia a questo:

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 sezione test controlla che message_details_dialog non è stato richiamato, che yes_no_dialog è stato richiamato, con il parametro di messaggio corretto, che Arecaceae è ancora lì:

# 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])

E così via

“ci sono due tipi di persone, coloro che completano quello che iniziano e così via”

Prossimo test è quasi la stessa, con la differenza che il utils.yes_no_dialog deve restituire True (questo raggiungiamo specificando result=True nell’applicazione parziale della mockfunc generica).

Con questa azione, il valore restituito da remove_callback dovrebbe essere True, e non ci dovrebbe essere nessuna famiglia Arecaceae nel database più:

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, [])

Date un’occhiata al commit 734f5bb9feffc2f4bd22578fcee1802c8682ca83 per le altre due funzioni di prova.

Prova registrazione

I nostri oggetti di bauble.test.BaubleTestCase utilizzano handler della classe bauble.test.MockLoggingHandler. Ogni volta che viene avviato un singolo unit test, il metodo setUp creerà un nuovo handler e lo associa al logger radice. Il metodo tearDown si prende cura di rimuoverlo.

È possibile controllare per la presenza di messaggi di registrazione specifici in self.handler.messages. messages è un dizionario, inizialmente vuoto, con due livelli di indicizzazione. Prima il nome del logger che rilascia la registrazione, quindi il nome del livello del record di registrazione. Le chiavi vengono create quando necessario. I valori contengono elenchi di messaggi formattati secondo qualsiasi formattatore si associa al gestore, per difetto logging.Formatter("%(message)s").

È possibile svuotare in modo esplicito i messaggi raccolti richiamando self.handler.clear().

Coordinamento

Di volta in volta si desidera attivare la classe di test che sta lavorando a:

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

E alla fine del processo si desidera aggiornare le statistiche:

./scripts/update-coverage.sh

Struttura dell’interfaccia utente

L’interfaccia utente è costruita secondo la modellovistaPresenter modello architettonico. Per gran parte dell’interfaccia, modello è un oggetto di database di SQLAlchemy, ma ci sono anche gli elementi dell’interfaccia dove non esiste un corrispondente modello di database. In generale:

  • Il vista è descritto come parte di un file glade. Ciò dovrebbe includere il segnale-callback e associazioni ListStore-TreeView. Basta utilizzare la classe base GenericEditorView definito in bauble.editor. Quando si crea l’istanza di questa classe generica, passarlo il glade nome del file e il nome del widget di radice, quindi consegnare questa istanza per il presentatore costruttore.

    Nel file glade, nella sezione action-widget chiusura tua descrizione oggetto GtkDialog, assicurarsi che ogni elemento di action-widget ha un valore valido response. Utilizza valori validi GtkResponseType, ad esempio:

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

    Non c’è un modo facile per scrivere prove unitarie per una sottoclasse vista, cui si prega non derivare viste, davvero non ce n’è alcun bisogno.

    Nel file glade, ogni widget input deve definire quale gestore viene attivato il segnale. La classe generica di Presenter offre callback generici che coprono il maggior parte dei casi.

    • GtkEntry (voce di testo a riga singola) gestirà il segnale changed, con on_text_entry_changed o on_unique_text_entry_changed.
    • GtkTextView: associarla a un GtkTextBuffer. Per gestire il segnale changed sulla GtkTextBuffer, dobbiamo definire un gestore che richiama il generico on_textbuffer_changed, l’unico ruolo per questa funzione è quello di passare il nostro gestore generico il nome dell’attributo modello che riceve il cambiamento. Si tratta di un workaroud per un bug irrisolto in GTK.
    • GtkComboBox con testi tradotti non può essere facilmente gestito dal file glade, così non ci proviamo. Utilizzare il metodo init_translatable_combo della classe GenericEditorView generica, ma si prega di richiamarlo dal presentatore.
  • Il modello è solo un oggetto con attributi noti. In questa interazione, la modello è solo un contenitore di dati passiva, che non si fa nulla di più che lasciare che la presentatore modificarlo.

  • La sottoclasse Presenter definisce e implementa:

    • “widget_to_field_map “, un dizionario associando i nomi widget a nome degli attributi di modello,
    • “view_accept_buttons “, l’elenco dei nomi di widget che, se attivato dall’utente, significa che la vista deve essere chiusa,
    • tutti gli accessori necessari i callback,
    • Facoltativamente, essa svolge la modello ruolo, troppo.

    Il presentatore aggiorna continuamente il modello in base alle modifiche nella vista. Se la modello corrisponde a un oggetto di database, la presentatore impegna tutti modello aggiornamenti al database quando il vista è chiuso correttamente, o il rollback se la vista viene annullata. (questo comportamento è influenzato dal parametro do_commit)

    Se il modello è un’altra cosa, il presentatore farà qualcos’altro.

    Nota

    Un presentatore ben educato utilizza la API della vista per interrogare i valori inseriti dall’utente o per impostare lo stato dei widget. Per favore non imparare dalla pratica dei nostri presentatori anomali, alcuni dei quali direttamente gestire campi di view.widgets. Così facendo, questi presentatori ci impedisce di scrivere unit test.

La classe base per il presentatore, GenericEditorPresenter definito in bauble.editor, implementa molte utili callback generici. C’è una classe di MockView, che è possibile utilizzare durante la scrittura di test per il tuo presentatori.

Esempi

Contact e ContactPresenter sono implementati seguendo le linee di cui sopra. La visualizzazione è definita nel file contact.glade.

Un buon esempio di modello di visualizzazione/relatore (nessun modello) è dato dalla gestione connessione.

Usiamo lo stesso schema architettonico per l’interazione non di database, impostando il presentatore anche come modello. Facciamo questo, ad esempio, per la finestra di dialogo Esporta JSON. Il seguente comando vi darà un elenco di istanze di GenericEditorView:

grep -nHr -e GenericEditorView\( bauble

Tavolo allungabile Ghini con plugin

Quasi tutto di Ghini è estensibile tramite plugin. Plugin può creare tabelle, definire ricerche personalizzate, aggiungere voci di menu, creare comandi personalizzati e altro ancora.

Per creare un nuovo plugin è necessario estendere la classe bauble.pluginmgr.Plugin.

Il plugin Tag è un buon esempio minimo, anche se il TagItemGUI rientra il pattern architetturale Model-View-Presenter.

Struttura del plugin

Ghini è un framework per la gestione delle collezioni e viene distribuito insieme a una serie di plugin rendendo Ghini un manager collezione botanica. Ma Ghini rimane un quadro e si potrebbe in teoria rimuovere tutti i plugin distribuiamo e Scrivi la tua, o scrivere il proprio plugin che estendono o completare il comportamento attuale di Ghini.

Una volta che avete selezionato e aperto una connessione al database, si terra nella finestra ricerca. La finestra di ricerca è un’interazione tra due oggetti: SearchPresenter (SP) e SearchView (SV).

SV è quello che vedete, SP detiene lo status di programma e gestisce le richieste che si esprimono attraverso SV. Gestire queste richieste influenzano il contenuto di SV e lo stato del programma in SP.

I risultati di ricerca illustrati nella parte più larga della SV sono righe, oggetti che sono istanze di classi registrate in un plugin.

Ciascuna di queste classi deve implementare un preciso insieme di funzioni per poter correttamente operare all’interno di Ghini. Ghini riserva spazio alle classi dei plugin.

SP sa di tutte le classi registrate (contenute nei plugin), sono memorizzate in un dizionario, che associa ciascuna classe alla relativa implementazione di plugin. SV ha uno slot (un gtk.Box) dove è possibile aggiungere elementi. In qualsiasi momento, al massimo un solo un elemento nello slot è visibile.

Un plugin definisce una o più classi di plugin. Una classe di plugin svolge il ruolo di un presentatore parziale (pP - plugin presenter) come implementare i callback necessari per la visualizzazione parziale associata raccordo nello slot (pV - plugin vista), e il modello MVP è completato dal presentatore padre (SP), ancora una volta funge da modello. Vediamo di riassumere e completare:

  • SP funge da modello,
  • la visualizzazione parziale di pV è definita in un file glade.
  • i callback implementati da pP sono nominati e riferiti nel file glade.
  • un menu di scelta rapida per la riga di SP,
  • una proprietà dipendente.

Quando si registra una classe di plugin, la SP:

  • aggiunge il pV nello slot e lo rende invisibile.
  • aggiunge un’istanza di pP nelle classi plugin registrati.
  • informa il pP che il modello è SP.
  • collega tutte le richiamate dal pV a pP.

Quando un elemento in pV genera un’azione in pP, pP può inoltrare l’azione a SP e può richiedere che SP aggiorni il modello e di conseguenza la vista.

Quando l’utente seleziona una riga in SP, SP nasconde tutto nello slot pluggable e mostra solo il pV corrispondente al tipo della riga selezionata e chiede al pP di aggiornare il pV con tutto ciò che è relativo alla riga selezionata.

Oltre a impostare la visibilità del pV vari, niente deve essere disabilitato né rimosso: un pV invisibile non può innescare eventi!

flusso di lavoro per risolvere bug

flusso di lavoro in condizioni normali

  • durante l’utilizzo del software, è possibile notare un problema, o si ottiene un’idea di qualcosa che potrebbe essere meglio, pensate a questo proposito abbastanza buono per avere un’idea molto chiara di quello che realmente è, che notato. si apre un problema e descrive il problema. qualcuno potrebbe reagire con sentori.
  • si apre il sito di problemi e scegliere uno che si vuole affrontare.
  • assegnare il problema a se stessi, in questo modo che si è informare il mondo che avete intenzione di lavorare a questo. qualcuno potrebbe reagire con sentori.
  • Facoltativamente Forkare il repository nel tuo account e preferibilmente creare un ramo, chiaramente associato al problema.
  • scrivere unit test e loro si impegnano a tua filiale (per favore non spingere in mancanza di unit test per github, eseguire prima la nosetests localmente).
  • scrivere più unit test (idealmente, le prove formano la descrizione completa della funzionalità aggiunta o correzione).
  • Assicurarsi che la funzionalità che si desidera aggiungere o correggere è davvero completamente descritto dagli unit test che hai scritto.
  • Assicurarsi che gli unit test sono atomici, cioè verificare variazioni sui cambiamenti lungo una singola variabile. non dare input complesso di unit test o test che non si adattano su uno schermo (25 righe di codice).
  • scrivere il codice che effettua i test riescono.
  • aggiornare i file i18n (Esegui ./scripts/i18n.sh).
  • Quando possibile, tradurre le nuove stringhe che metti nei file di codice o radura.
  • quando cambi qualche stringa, assicurati che le sue vecchie traduzioni vengano ancora utilizzate.
  • confermare le modifiche.
  • spingere a github.
  • aprire una richiesta di pull.

pubblicazione in produzione

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:

  • Aprire il pull richiesta pagina utilizzando come base una linea di produzione ghini-x.y, rispetto al ghini-x.y-dev.
  • Assicurarsi che un commit bump è incluso nelle differenze.
  • dovrebbe essere possibile unire automaticamente i rami.
  • creare la nuova richiesta di pull, lo chiamano come «pubblica la linea di produzione».
  • Forse devi aspettare per travis-ci eseguire i controlli.
  • unire le modifiche.

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.

your own fork

If you want to keep your own fork of the project, keep in mind this is full force work in progress, so staying up to date will require some effort from your side.

The best way to keep your own fork is to focus on some specific issue, work relatively quickly, often open pull requests for your work, make sure that you get it accepted. Just follow Ghini’s coding style, write unit tests, concise and abundant, and there should be no problem in having your work included in Ghini’s upstream.

If your fork got out of sync with Ghini’s upstream: read, understand, follow the github guides configuring a remote for a fork and syncing a fork.

Fase di

  • Scrivi una recensione su questo flusso di lavoro. considerare questo come una linea guida, a voi stessi e ai vostri colleghi. si prega di contribuire a rendere migliore e la pratica di corrispondenza.

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 will not work with eggs

Building a Windows executable with py2exe requires packages not be installed as eggs. There are several methods to accomplish this, including:

  • 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.