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:
FigureManagerBase
e suas classes derivadas na classe de funcionalidade principalFigureManager
e uma classe específica de back-endWindowBase
eShowBase
e suas classes derivadas emGcf.show_all
eMainLoopBase
.
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_helpers
e 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
FigureManagerBase
e ShowBase
.
FigureManagerBase
tem três empregos no momento:A documentação o descreve como uma classe Helper para o modo pyplot, agrupa tudo em um pacote organizado
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
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.
ShowBase
tem dois empregos:Ele tem a função de passar por todas as figuras dos gerentes cadastrados
_pylab_helpers.Gcf
e dizer para eles se apresentarem.E em segundo lugar tem a função de realizar o backend específico
mainloop
para 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:
Para remover o aspecto de janela
FigureManagerBase
, basta agrupar essa nova classe junto com as outras classes de back-end. Crie uma novaWindowBase
classe que possa lidar com essa funcionalidade, com métodos de passagem (:arrow_right:) paraWindowBase
. Classes que subclasseWindowBase
também devem subclasse a classe de janela específica da GUI para garantir a compatibilidade com versões anteriores ( ).manager.window == manager.window
Refatore o mainloop de
ShowBase
intoMainLoopBase
, que também encapsula o final do loop. Damos uma instância deMainLoop
comoFigureManager
uma 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.Agora que
FigureManagerBase
não há especificações de back-end nele, renomeie-o paraFigureManager
e mova para um novo arquivobackend_managers.py
observando que: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.E isso também antecipa o MEP22, onde o novo
NavigationBase
se transformou em um back-end independenteToolManager
.
FigureManagerBase(canvas, num) |
FigureManager(figura, num) |
|
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
NavigationToolbar2
muito direta, precisando apenas tocar na FigureManager
classe ú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.resize
redimensionam 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 FigureCanvasWx
construtor, 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
|
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 FigureManager
també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.