MEP27: desacoplar pyplot de backends #

Estado #

Progresso

Filiais e solicitações pull #

PR principal (incluindo GTK3):

Diferenças de ramificação específicas do back-end:

Resumo #

Este MEP refatora os back-ends para fornecer uma API mais estruturada e consistente, removendo o código genérico e consolidando o código existente. Para fazer isso, propomos a divisão:

  1. FigureManagerBasee suas classes derivadas na classe de funcionalidade principal FigureManagere uma classe específica de back-end WindowBasee

  2. ShowBasee suas classes derivadas em Gcf.show_alle MainLoopBase.

Descrição detalhada #

Este MEP visa consolidar a API de back-ends em uma única API uniforme, removendo o código genérico do back-end (que inclui _pylab_helperse Gcf) e empurrando o código para um nível mais apropriado no matplotlib. Com isso removemos automaticamente as inconsistências que aparecem nos backends, como por exemplo, que ora configura a tela, ora configura toda a janela para as dimensões dadas, dependendo do backend.FigureManagerBase.resize(w, h)

Dois locais principais para código genérico aparecem nas classes derivadas de FigureManagerBasee ShowBase.

  1. FigureManagerBasetem três empregos no momento:

    1. A documentação o descreve como uma classe Helper para o modo pyplot, agrupa tudo em um pacote organizado

    2. Mas ele não envolve apenas a tela e a barra de ferramentas, ele também faz todas as tarefas de janelamento. A fusão dessas duas tarefas é vista melhor na seguinte linha: Isso combina código específico de back-end com código genérico matplotlib .self.set_window_title("Figure %d" % num)self.set_window_title(title)title = "Figure %d" % num

    3. Atualmente, a subclasse específica de backend de FigureManager decide quando terminar o mainloop. Isso também parece muito errado, pois a figura não deve ter controle sobre as outras figuras.

  2. ShowBasetem dois empregos:

    1. Ele tem a função de passar por todas as figuras dos gerentes cadastrados _pylab_helpers.Gcfe dizer para eles se apresentarem.

    2. E em segundo lugar tem a função de realizar o backend específico mainlooppara bloquear o programa principal e assim evitar que as figuras morram.

Implementação #

A descrição deste MEP nos dá a maior parte da solução:

  1. Para remover o aspecto de janela FigureManagerBase, basta agrupar essa nova classe junto com as outras classes de back-end. Crie uma nova WindowBaseclasse que possa lidar com essa funcionalidade, com métodos de passagem (:arrow_right:) para WindowBase. Classes que subclasse WindowBasetambém devem subclasse a classe de janela específica da GUI para garantir a compatibilidade com versões anteriores ( ).manager.window == manager.window

  2. Refatore o mainloop de ShowBaseinto MainLoopBase, que também encapsula o final do loop. Damos uma instância de MainLoopcomo FigureManageruma chave desbloquear o método de saída (exigindo que todas as chaves sejam retornadas antes que o loop possa morrer). Observe que isso abre a possibilidade de vários back-ends serem executados simultaneamente.

  3. Agora que FigureManagerBasenão há especificações de back-end nele, renomeie-o para FigureManagere mova para um novo arquivo backend_managers.pyobservando que:

    1. Isso nos permite dividir a conversão de back-ends em PRs separados, pois podemos manter a FigureManagerBase classe existente e suas dependências intactas.

    2. E isso também antecipa o MEP22, onde o novo NavigationBasese transformou em um back-end independente ToolManager.

FigureManagerBase(canvas, num)

FigureManager(figura, num)

WindowBase(title)

Notas

mostrar

mostrar

destruir

chama destruir em todos os componentes

destruir

full_screen_toggle

lida com a lógica

set_fullscreen

redimensionar

redimensionar

pressione o botão

pressione o botão

get_window_title

get_window_title

set_window_title

set_window_title

_get_toolbar

Um método comum a todas as subclasses de FigureManagerBase

set_default_size

add_element_to_window

ShowBase

MainLoopBase

Notas

circuito principal

começar

fim

É chamado automaticamente quando não existem mais instâncias da subclasse

__ligar__

Método movido para Gcf.show_all

Compatibilidade futura #

Conforme explicado acima ao discutir o MEP 22, esse refator torna mais fácil adicionar novos recursos genéricos. No momento, o MEP 22 tem que fazer hacks feios para cada classe que vai de FigureManagerBase. Com este código, isso só precisa ser feito na FigureManager classe única. Isso também torna a descontinuação posterior NavigationToolbar2muito direta, precisando apenas tocar na FigureManagerclasse única

O MEP 23 é outro caso de uso em que esse código refatorado será muito útil.

Compatibilidade com versões anteriores #

Como deixamos todo o código de back-end intacto, apenas adicionando métodos ausentes às classes existentes, isso deve funcionar perfeitamente para todos os casos de uso. A única diferença recairá sobre os backends que FigureManager.resizeredimensionam a tela e não a janela, devido à padronização da API.

Eu imaginaria que as classes tornadas obsoletas por este refatorador seriam obsoletas e removidas no mesmo horário que NavigationToolbar2, observe também que a alteração na assinatura de chamada para o FigureCanvasWxconstrutor, embora compatível com versões anteriores, acho que a assinatura antiga (estilo feio) deveria ser obsoleta e removido da mesma maneira que todo o resto.

Processo interno

gerenciador.resize(w,h)

Extra

gtk3

janela

Tk

tela

Qt

janela

Wx

tela

FigureManagerWx tinha framecomo um alias para window, então isso também quebra o BC.

Alternativas #

Se houvesse soluções alternativas para resolver o mesmo problema, elas deveriam ser discutidas aqui, juntamente com uma justificativa para a abordagem escolhida.

Perguntas #

Mdehoon: Você pode elaborar sobre como executar vários back-ends simultaneamente?

OceanWolf: @mdehoon, como eu disse, não para este MEP, mas vejo que este MEP abre como uma possibilidade futura. Basicamente a MainLoopBase classe atua como um Gcf por backend, neste MEP ela rastreia o número de figuras abertas por backend e gerencia os mainloops para esses backends. Ele fecha o mainloop específico do backend quando detecta que nenhuma figura permanece aberta para esse backend. Por causa disso, imagino que com apenas uma pequena quantidade de ajustes podemos fazer matplotlib full-multi-backend. Ainda não faço ideia de por que alguém iria querer, mas deixo a possibilidade lá no MainLoopBase. Com todas as especificidades do código de back-end refatoradas FigureManagertambém ajudam nisso, um gerente para governá-los (os back-ends).

Mdehoon: @OceanWolf, OK, obrigado pela explicação. Ter uma API uniforme para os back-ends é muito importante para a manutenção do matplotlib. Acho que este deputado é um passo na direção certa.