Manual do desenvolvedor¶
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.
Helping Ghini development¶
Se você quiser contribuir para Ghini, você pode fazer isso de várias maneiras diferentes:
- Usar o software, observe as coisas que você não gosta, abrir uma questão para cada um deles. Um desenvolvedor irá reagir mais cedo do que imagina.
- Se você tem uma ideia de o que falta no software, mas não posso formalizar completamente em questões distintas, você poderia considerar a contratação de um profissional. Esta é a melhor maneira de fazer com que algo aconteça rapidamente na Ghini. Verifique se o desenvolvedor abre questões e publica sua contribuição no github.
- Traduzir! Qualquer ajuda com traduções serão bem-vindas, então faça-o! Você pode fazer isso sem instalar nada no seu computador, usando o serviço de tradução on-line oferecidos pela http://hosted.weblate.org/
- bifurque o repositório escolha um tema, resolva-lo, abra uma solicitação de tração. Consulte o bug resolver o fluxo de trabalho abaixo.
Se você ainda não instalou o Ghini e quero dar uma olhada em sua história de código, você pode abrir nossa página de projeto do github e ver tudo o que está acontecendo ao redor Ghini desde a sua criação como Bauble, volta no ano de 2004.
If you install the software according to the devinstall
instructions,
you have the whole history in your local git clone.
Fonte do software, versões, ramos¶
Se quiser uma versão particular do Ghini, podemos liberar e manter versões como ramos. Deve git checkout
o ramo correspondente à versão da sua escolha.
linha de produção¶
Nomes de ramo para Ghini estável (produção) versões são do formulário ghini-x. y
(por exemplo: ghini-1.0); nomes do ramo onde Ghini versões de teste são publicadas são da forma ghini-x. y-dev
(por exemplo: ghini-1.0-dev).
Fluxo de trabalho de desenvolvimento¶
Nosso fluxo de trabalho é empenhar-se continuamente para o ramo de teste, para muitas vezes, empurrá-los para o github, deixar travis-ci e coveralls.io verificar a qualidade dos ramos testes pressionados, finalmente, de vez em quando, para mesclar o ramo de teste para a versão correspondente.
Quando se trabalha em questões maiores, que parecem demorar mais uns dias, eu poderia abrir uma filial associada à questão. Não faço isto muitas vezes.
questões mais importantes¶
Quando enfrenta uma única questão maior, criar uma marca de ramificação na ponta de uma linha de desenvolvimento principal (por exemplo: ghini-1.0-dev
) e siga o fluxo de trabalho descrito no
https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging
em suma
git up
git checkout -b issue-xxxx
git push origin issue-xxxx
Trabalho no ramo novo temporário. Quando estiver pronto, vá para o github, mesclar o ramo com a linha de desenvolvimento principal do qual você ramificada, resolver conflitos, sempre que necessário, apagar o ramo temporário.
Quando estiver pronto para publicação, mescle a linha de desenvolvimento em linha de produção correspondente.
Atualizando o conjunto de cadeias de caracteres traduzíveis¶
Por vezes, durante o processo de atualização do software, vai ser adicionando ou modificando cordas nas fontes python, na documentação, nas fontes glade. A maioria das nossas cadeias são traduzíveis e são oferecidas ao weblate para as pessoas a contribuir, sob a forma de vários ficheiros po
.
Um “po “ é principalmente composta por pares de porções de texto, original e Tradução e é específico para um idioma de destino. Quando um tradutor acrescenta uma tradução em weblate, isso atinge o nosso repositório no github. Quando um programador adiciona uma cadeia de caracteres ao software, atinge o weblate como «to ser translated».
Weblate hospeda o Ghini projeto. Dentro deste projeto temos componentes, cada uma das quais corresponde a um ramo de um repositório no github. Cada componente aceita traduções em várias línguas.
component | repository | 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 |
Para atualizar os ficheiros po
relativo ao * software , define o diretório de trabalho para a raiz do seu check-out de * ghini.desktop e executar o script:
./scripts/i18n.sh
Para atualizar os ficheiros po
relativo à * documentação , define o diretório de trabalho para a raiz do seu check-out de *ghini.desktop-docs.i18n, e execute o script:
./doc/runme.sh
Quando executar qualquer um dos scripts acima mencionadas, as hipóteses são de precisa cometer todo os ficheiros po
no projeto. Pode querer rever as alterações antes de confirmá-las para o repositório. Isso é mais importante quando você executar uma correção marginal para uma cadeia de caracteres, como remover um erro de digitação.
Something that happens: running into a conflict. Solving conflicts is not difficult once you know how to do that. First of all, add weblate as remote:
git remote add weblate-doc10 https://hosted.weblate.org/git/ghini/documentation-10/
Then make sure we are in the correct repository, on the correct branch, update the remote, merge with it:
git checkout ghini-1.0-dev
git remote update
git merge weblate-doc10/ghini-1.0-dev
Nossa documentação na readthedocs tem uma versão original em inglês e várias traduções. Basta seguir a Descrição da localização, não há nada que nós mesmos inventamos aqui.
Readthedocs verifica do projeto * idioma * configuração e invoca o sphinx-intl
para produzir a documentação formatada na língua-alvo. Com a configuração padrão — que não alteramos — sphinx-intl
espera um ficheiro de po
por documento de origem, nomeado como o documento de origem, e que todos eles residem no diretório local / $(LANG) /LC_MESSAGES/
.
Por outro lado, Weblate (nós mesmos) prefere um ficheiro único po
por idioma e mantém-los todos no mesmo diretório /po
, assim como fazemos para o projeto de software: /po/$ (LANG) po
.
Em ordem para não repetir informações e deixar ambos sistemas trabalham seu caminho natural, temos dois conjuntos de links simbólicos (git homenageia-los).
Para resumir: quando um ficheiro na documentação é atualizado, o runme.sh
script será:
- Copie os ficheiros de
rst
do software à documentação; - criar um novo ficheiro de
pot
para cada um dos ficheiros de documentação; - mesclar todos os ficheiros
pot
num “doc.pot “; - usar o
doc.pot
atualizado para atualizar todos os ficheirosdoc.po
(um por idioma); - Crie todos os links simbólicos:
- aqueles esperados pelo “sphinx-intl
em
/local/$ (LANG) /LC_MESSAGES/ “ - aqueles usados pelo weblate em “/po/$ (LANG) po “
- aqueles esperados pelo “sphinx-intl
Definitivamente poderíamos escrever o acima num Makefile, ou melhor ainda, incluí-lo em “/ doc/Makefile “. Quem sabe, talvez o faremos.
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.
Adicionando testes de unidade faltando¶
Se você está interessado em contribuir para o desenvolvimento de Ghini, uma boa maneira de fazê-lo seria nos ajudar a encontrar e escrever os testes de unidade faltando.
Uma função bem testada é aquele cujo comportamento não pode mudar sem quebrar pelo menos um teste de unidade.
Todos concordamos que em teoria teoria e prática combinam perfeitamente e que um primeiro escreve os testes, em seguida, implementa a função. Na prática, no entanto, prática não corresponde a teoria e estamos escrevendo testes depois de escrever e publicar até as funções.
Esta secção descreve o processo de adição de testes unitários para “bauble.plugins.plants.family.remove_callback “.
O que testar¶
Primeiro de tudo, abra o índice do relatório de cobertura e escolher um ficheiro com baixa cobertura.
Para este exemplo, execute em outubro de 2015, pousamos na “bauble.plugins.plants.family “, em 33%.
https://coveralls.io/builds/3741152/source?filename=bauble%2Fplugins%2Fplants%2Ffamily.py
As duas primeiras funções que precisam de testes, edit_callback
e add_genera_callback
, incluem a criação e ativação de um objeto depender de uma caixa de diálogo personalizada. Nós deveríamos realmente primeiro escrever testes de unidade para essa classe, depois volte aqui.
A função seguinte, remove_callback
, também ativa um par de caixas de diálogo e mensagem, mas sob a forma de chamar uma função, solicitando a entrada do utilizador através de caixas de Sim-não-okey. Essas funções podemos facilmente substituir com uma função de gozar com o comportamento.
Funcionamento¶
Então, tendo decidido o que descrevem no teste de unidade, olhamos para o código e vemos que é necessário discriminar alguns casos:
- parâmetro correção
- a lista das famílias não tem nenhum elemento.
- a lista das famílias tem mais de um elemento.
- a lista das famílias tem exatamente um elemento.
- cascade
- a família não tem nenhum gêneros
- a família tem um ou mais gêneros
- confirm
- o utilizador confirma a exclusão
- o utilizador não confirma a exclusão
- deleting
- Tudo vai bem quando a exclusão da família
- Há algum erro ao excluir a família
Eu decido só incidirá sobre o cascata e o confirmar aspectos. Duas perguntas binárias: 4 casos.
onde colocar os testes¶
Localize o script de teste e escolher a classe onde colocar os testes de unidade extra.
https://coveralls.io/builds/3741152/source?filename=bauble%2Fplugins%2Fplants%2Ftest.py#L273
what about skipped tests
ClasseFamilyTests
contém um teste saltado, implementá-lo vai ser um pouco de trabalho porque precisamos reescrever a FamilyEditorPresenter, separá-lo do FamilyEditorView e reconsiderar o que fazer com a classe FamilyEditor, que eu acho que deve ser removido e substituído com uma única função.
escrevendo os testes¶
Após o último teste na classe FamilyTests, adicionar os quatro casos que quero descrever, e garantir que eles falham, e desde que eu sou preguiçoso, eu escrevo o código mais compacto sei para gerar um erro:
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
Um teste, passo a passo¶
Vamos começar com o primeiro caso de teste.
Ao escrever testes, eu geralmente seguem o padrão:
- T₀ (condição inicial),
- action,
- T₁ (testar o resultado da ação dada as condições iniciais)
what’s in a name — unit tests
Há uma razão por que testes de unidade são chamados de testes de unidade. Por favor, nunca teste duas ações num teste.
Então, vamos descrever T₀ para o primeiro teste, um banco de dados mantém uma família sem gêneros:
def test_remove_callback_no_genera_no_confirm(self):
f5 = Family(family=u'Arecaceae')
self.session.add(f5)
self.session.flush()
Não queremos a função que está a ser testada para invocar a função interativa “utils.yes_no_dialog “, queremos remove_callback
para invocar uma função de substituição não-interativo. Conseguimos isso simplesmente por fazer ponto utils.yes_no_dialog
para uma expressão de lambda
que, assim como a função original e interativa, aceita um parâmetro e retorna um valor booleano. Neste caso: falso
:
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)
Em seguida, testamos o resultado.
Bem, não só queremos testar ou não o objeto Arecaceae foi apagado também devemos testar o valor retornado por “remove_callback “, e se yes_no_dialog
e message_details_dialog
foram invocados ou não.
Uma expressão de lambda
não é suficiente para isso. Fazemos algo aparentemente mais complexo, o que tornará a vida mais fácil.
Vamos primeiro definir uma função bastante genérica:
def mockfunc(msg=None, name=None, caller=None, result=None):
caller.invoked.append((name, msg))
return result
e vamos comer parcial
do functools
módulo padrão, parcialmente aplicar o acima mockfunc
, deixando apenas msg
não for especificado, e usar esta aplicação parcial, que é uma função que aceita um parâmetro e retornar um valor, para substituir as duas funções no “utils “. A função de teste agora se parece com isto:
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()
A secção de teste verifica que message_details_dialog
não foi invocado, que foi chamado yes_no_dialog
, com o parâmetro mensagem correta, que Arecaceae ainda está 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 assim por diante.¶
“há dois tipos de pessoas, aqueles que completam o que começam e assim por diante”
Próximo teste é quase o mesmo, com a diferença que o utils.yes_no_dialog
deve retornar True
(isto conseguirmos especificando resultado = True
na aplicação parcial do generic mockfunc
).
Com esta ação, o valor retornado por remove_callback
deve ser True
, e não deve haver nenhuma família Arecaceae no banco de dados mais:
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, [])
Dê uma olhada no 734f5bb9feffc2f4bd22578fcee1802c8682ca83 de confirmação para as outras duas funções de teste.
Log de teste¶
Nossos objetos de bauble.test.BaubleTestCase
usam manipuladores de classe bauble.test.MockLoggingHandler
. Cada vez que um teste de unidade individual é iniciado, o método setUp
irá criar um manipulador de novo e associá-lo para o agente de log de raiz. O método tearDown
se encarrega de removê-lo.
Você pode verificar a presença de mensagens de log específico em “self.handler.messages “. mensagens
são um dicionário, inicialmente vazio, com dois níveis de indexação. Primeiro o nome do agente de log emitir o registro registro, então o nome do nível do registro de log. As chaves são criadas quando necessário. Valores de manter listas de mensagens, formatadas de acordo com o formatador você associar ao manipulador, padronizando a log. Formatter("%(message)s")
.
Você explicitamente pode esvaziar as mensagens coletadas invocando self.handler.clear()
.
Resumindo tudo¶
De vez em quando você deseja ativar a classe de teste que você está trabalhando no:
nosetests bauble/plugins/plants/test.py:FamilyTests
E no final do processo que você deseja atualizar as estatísticas:
./scripts/update-coverage.sh
Estrutura da interface de utilizador¶
A interface do utilizador é construída de acordo com o modelo — * * vista * * — apresentador padrão arquitetural. Para grande parte da interface, modelo é um objeto de banco de dados do SQLAlchemy, mas também temos elementos de interface onde não há nenhum modelo de banco de dados correspondente. Em geral:
O vista é descrita como parte de um ficheiro glade. Isto deve incluir a chamada de retorno do sinal e associações ListStore-TreeView. Só reutilize a classe base
GenericEditorView
definido em “bauble.editor “. Quando você criar a instância dessa classe genérica, passe-lo o nome do ficheiro glade e o nome do elemento raiz, então entregue essa instância para o construtor presenter.No ficheiro glade, na secção
ação-widgets
fechando a sua descrição de objeto GtkDialog, certifique-se de que cada elemento deação-widget
tem um valor válido “resposta “. Uso valores válidos de GtkResponseType, por exemplo:- GTK_RESPONSE_OK, -5
- GTK_RESPONSE_CANCEL, -6
- GTK_RESPONSE_YES, -8
- GTK_RESPONSE_NO, -9
Não há nenhuma maneira fácil de teste de unidade «subclasse» um modo de exibição, então por favor não vistas de subclasse, não há realmente nenhuma necessidade de.
No ficheiro glade, cada entrada widget deve definir qual manipulador é ativado na qual sinal. Classe genérica do apresentador oferece retornos de chamada genéricos que cobrem os casos mais comuns.
- GtkEntry (entrada de texto de uma linha) irá lidar com o sinal
changed
, comon_text_entry_changed
ouon_unique_text_entry_changed
. - GtkTextView: associá-la a um GtkTextBuffer. Para lidar com o sinal
changed
sobre o GtkTextBuffer, nós temos que definir um manipulador que invoca o genéricoon_textbuffer_changed
, a única função para esta função é para transmitir nosso manipulador genérico o nome do atributo de modelo que recebe o troco. Este é um workaroud para um bug não resolvido em GTK. - GtkComboBox com textos traduzidos não pode ser facilmente manipulado do ficheiro glade, então nós nem tente. Use o método
init_translatable_combo
da classe genéricaGenericEditorView
, mas por favor, invocá-lo do * *. * * apresentador.
O modelo é só um objeto com atributos. Nessa interação, o modelo é apenas um contentor de dados passivo, ele não faz nada mais do que deixar a apresentador modificá-lo.
O que foi feito subclassing apresentador define e implementa:
- “widget_to_field_map “, um dicionário associando nomes de widget para nome de atributos do modelo,
- “view_accept_buttons “, a lista de nomes de widget que, se ativado pelo utilizador, significa que o modo de exibição deve ser fechado,
- Tudo o que precisava de retornos de chamada,
- Opcionalmente, ele joga o papel de modelo, também.
O apresentador atualiza continuamente o modelo de acordo com as mudanças na vista. Se o modelo corresponde a um objeto de banco de dados, o apresentador confirma tudo modelo atualizações no banco de dados quando o vista é encerrada com êxito, ou reverte-los se o vista é cancelada. (este comportamento é influenciado pelo parâmetro
do_commit
)Se o modelo é outra coisa, então o apresentador vai fazer outra coisa.
Nota
Um bem-comportado apresentador usa a vista api para consultar os valores inseridos pelo utilizador ou forçosamente definir estados do widget. Por favor, não aprenda com a prática dos nossos apresentadores de comportamento inadequados, alguns dos quais diretamente manipular campos de “view.widgets “. Ao fazer isso, esses apresentadores nos impede de escrever testes de unidade.
A classe base para o apresentador, GenericEditorPresenter
definido em “bauble.editor “, implementa muitos retornos de chamada genéricos úteis. Há uma classe de MockView
, que você pode usar ao escrever testes para seus apresentadores.
Examples¶
Contato
e ContactPresenter
são implementados seguindo as linhas acima. A exibição é definida no ficheiro contact.glade
.
Um bom exemplo de padrão de exibição do apresentador (sem modelo) é dado pelo gestor de conexões.
Nós usamos o mesmo padrão arquitetônico para a interação não-banco de dados, definindo o apresentador também como modelo. Fazemos isto, por exemplo, para a caixa de diálogo de exportação JSON. O comando a seguir lhe dará uma lista de instanciações de GenericEditorView
:
grep -nHr -e GenericEditorView\( bauble
Estendendo Ghini com Plugins¶
Nearly everything about Ghini is extensible through plugins. Plugins can create tables, define custom searchs, add menu items, create custom commands and more.
Para criar um novo plugin, você deve estender a classe bauble.pluginmgr.Plugin
.
O plugin Tag
é um bom exemplo de mínimo, mesmo que o TagItemGUI
cai fora o padrão arquitetural Model-View-Presenter.
Estrutura de plugins¶
Ghini é um framework para a manipulação de coleções e é distribuído juntamente com um conjunto de plugins fazendo Ghini um gestor de colecções botânicas. Mas Ghini fica um quadro e você poderia em teoria remova todos os plugins vamos distribuir e escrever seus próprios, ou escrever seus próprios plugins que estender ou completar o actual comportamento Ghini.
Uma vez que você selecionou e abriu uma conexão de banco de dados, você pousar na janela de pesquisa. A janela de busca é uma interação entre dois objetos: SearchPresenter (SP) e SearchView (SV).
SV é o que vê, SP detém o estado de programa e manipula as solicitações que expressa através de SV. Manipulando essas solicitações afetam o conteúdo de SV e o estado de programa em SP.
Resultados da pesquisa aparece na parte maior do SV são linhas, objetos que são instâncias de classes registados num plugin.
Cada uma dessas classes deve implementar uma quantidade de funções para comportar-se adequadamente no âmbito Ghini. O quadro Ghini reserva espaço para classes conectáveis.
SP sabe de todas as classes registadas (conectadas em), eles são armazenados num dicionário, associando uma classe para implementação dele do plugin. SV tem um slot (um gtk. Caixa) onde pode adicionar elementos. A qualquer hora, no máximo apenas um elemento na ranhura é visível.
Um plugin define uma ou mais classes de plugin. Um plugin de classe desempenha o papel de um apresentador parcial (pP - plugin apresentador) como ele implementar os retornos de chamada necessários pela exibição parcial do associado encaixe na ranhura (pV - plugin vista), e o padrão MVP é completado pelo apresentador pai (SP), mais uma vez atuando como modelo. Para resumir e concluir:
- SP atua como modelo,
- a exibição parcial do pV é definida num ficheiro glade.
- os retornos de chamada implementados pelo pP são referenciados pelo ficheiro glade.
- um menu de contexto para a linha de SP,
- uma propriedade de crianças.
Quando você registra uma classe de plugin, o SP:
- Adiciona o pV no slot e o torna não-visíveis.
- Adiciona uma instância de pP nas classes plugin registrado.
- diz a pP que o SP é o modelo.
- conecta todos os retornos de chamada de pV para pP.
Quando um elemento em pV desencadeia uma ação em pP, pP pode encaminhar a ação para SP e pode solicitar SP que atualiza o modelo e atualiza a exibição.
Quando o utilizador seleciona uma linha em SP, SP esconde tudo na ranhura conectável mostra apenas o único pV em relação o tipo da linha selecionada e pede o pP para atualizar o pV com tudo o que é relativo a linha selecionada.
Além de definir a visibilidade de vários pV, nada precisa ser desativado ou removido: um pV invisível não pode disparar eventos!
Bug, solução de fluxo de trabalho¶
fluxo de trabalho de desenvolvimento normal¶
- ao usar o software, você notar um problema, ou você ter uma ideia de algo que poderia ser melhor, pensa nisso bom o suficiente para se ter uma ideia muito clara do que realmente é, que você notou. Você abre uma questão e descreve o problema. Alguém pode reagir com dicas.
- Você abre o site de problemas e escolher que quer combater.
- Atribua o problema a mesmo, desta forma que você está informando ao mundo que você tem a intenção de trabalhar nisto. Alguém pode reagir com dicas.
- bifurque, opcionalmente, o repositório na sua conta e de preferência, criar uma ramificação, claramente associada à questão.
- escrever testes de unidade e comprometê-los com sua filial (por favor, não empurre na ausência de testes de unidade para o github, executar
nosetests
localmente primeiro). - escrever testes de unidade mais (idealmente, os testes de formam a descrição completa do recurso estiver adicionando ou corrigindo).
- Certifica-se de que o recurso que você está adicionando ou corrigindo é realmente completamente descrito pelos testes de unidade que você escreveu.
- Certifique-se que os testes unitários são atômicos, ou seja, que testar variações sobre alterações ao longo de uma única variável. Não dê entrada complexa para testes de unidade ou testes que não cabem num único ecrã (25 linhas de código).
- Escreva o código que faz com que seus testes bem sucedidos.
- Atualize os ficheiros de i18n (executados
./scripts/i18n.sh
). - sempre que possível, traduza as novas cadeias de caracteres que põe em ficheiros de código ou glade.
- Quando você alterar a cadeia de caracteres, por favor, certifique-se de que antigas traduções me re-acostumar.
- Confirme as alterações.
- Empurre para o github.
- Abra uma solicitação de tração.
publicação a produção¶
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:
- Abra o puxar solicitação página usando como base uma linha de produção “ghini-x. y “, em relação ao “ghini-x. y-dev “.
- Certifique-se de que um commit
galo
está incluído nas diferenças. - é possível mesclar automaticamente os ramos.
- criar a nova solicitação de tração, chamá-lo como «publicar para a linha de produção».
- você possivelmente precisar esperar por travis-ci executar as verificações.
- Mescle as alterações.
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.
Encerramento¶
- Examine este fluxo de trabalho. Considere isto como uma diretriz, a mesmo e aos seus colegas. por favor ajude a torná-lo melhor e a prática de correspondência.
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
.