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 |
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:
- copiare i file
rst
dal software alla documentazione; - creare un nuovo file
pot
per ciascun elemento di documentazione; - unire tutti i file
pot
in un unicodoc.pot
; - aggiornare tutti i
doc.po
(uno per lingua) sulla base del nuovodoc.pot
; - creare tutti i collegamenti simbolici:
- quelli utilizzati da
sphinx-intl
in/local/$(LANG)/LC_MESSAGES/
- quelli utilizzati da noi e weblate in
/po/$(LANG).po
- quelli utilizzati da
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 classeFamilyTests
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 modello — vista — Presenter 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 inbauble.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 diaction-widget
ha un valore validoresponse
. 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
, conon_text_entry_changed
oon_unique_text_entry_changed
. - GtkTextView: associarla a un GtkTextBuffer. Per gestire il segnale
changed
sulla GtkTextBuffer, dobbiamo definire un gestore che richiama il genericoon_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 classeGenericEditorView
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 alghini-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 bypy2exe
, used bynsis
.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:
Download and install Git, Python 2.7 and PyGTK.
This is outlined in the
devinstall
-based installation instructions.Download and install NSIS v3.
A reboot is recommended.
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 branchghini-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:
/e
— executable only.Produce an executable only, skipping the extra step of building an installer, and will copy
win_gtk.bat
into place.
venv_path
— A path to the location for the virtual environment to use.Defaults to
"%HOMEDRIVE%%HOMEPATH%"\.virtualenvs\%CHECKOUT%-exe
, whereCHECKOUT
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 usingpip install .
as described below. If you do wish to install over the top of an install with eggs (e.g. the environment created bydevinstall.bat
) you can trypip install -I .
but your mileage may vary.By adding:
[easy_install] zip_ok = Falseto setup.cfg (or similarly
zip_safe = False
tosetuptools.setup()
insetup.py
) you can usepython 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 thepip
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.batOn 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
(runpython --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 py2exeThe
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 clickghini.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 theghini-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 nsisThis should leave a file named
ghini.desktop-<version>-setup.exe
in thedist
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
.