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
Pocket 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á:

  1. Copie os ficheiros de rst do software à documentação;
  2. criar um novo ficheiro de pot para cada um dos ficheiros de documentação;
  3. mesclar todos os ficheiros pot num “doc.pot “;
  4. usar o doc.pot atualizado para atualizar todos os ficheiros doc.po (um por idioma);
  5. Crie todos os links simbólicos:
    1. aqueles esperados pelo “sphinx-intl em /local/$ (LANG) /LC_MESSAGES/ “
    2. aqueles usados pelo weblate em “/po/$ (LANG) po “

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

Classe FamilyTests 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 de açã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, com on_text_entry_changed ou on_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érico on_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érica GenericEditorView, 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 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.