Avançar para a área de conteúdo

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Todas as informações enviadas são seguras.

  • Fechar [x]

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

Todas as informações enviadas são seguras.

  • Fechar [x]

Gerando script KVM com Python, Parte 2: Adicione uma GUI para gerenciar KVM com libvirt e Python

Paul Ferrill, CTO, ATAC
Paul Ferrill escreve nos meios de comunicação de comércio de computadores há mais de 20 anos. Começou escrevendo resenhas de rede para o PC Magazine sobre produtos como LANtastic e versões anteriores do Novell Netware. Paul é graduado e possui mestrado em engenharia elétrica e escreveu softwares para mais arquiteturas e plataformas de computador do que consegue se lembrar.

Resumo:  Esta série de duas partes explora como usar Python para criar scripts para gerenciar máquinas virtuais usando KVM. Neste artigo, você aprende com adicionar uma GUI para expandir a ferramenta simples de status e exibição.

Visualizar mais conteúdo nesta série

Data:  10/Fev/2012
Nível:  Intermediário Também disponível em :   Inglês
Atividade:  494 visualizações
Comentários:  


A Parte 1 desta série analisou as noções básicas de script de Kernel-based Virtual Machine (KVM) usando libvirt e Python. Este artigo usa os conceitos desenvolvidos na primeira parte para desenvolver diversos aplicativos de utilitário e adicionar uma interface gráfica com o usuário (GUI) na combinação. Há duas opções principais para um kit de ferramentas de GUI com ligações de Python e que serve para várias plataformas. A primeira é Qt, que agora pertence à Nokia; a segunda é wxPython. Ambas têm seguidores fervorosos e muitos projetos de software livre em suas listas de usuários.

Para este artigo, eu me concentro no wxPython, mas por preferência pessoa do que qualquer coisa. Eu começo com uma breve introdução ao wxPython e a configuração básica apropriada. A partir daí, passo para alguns programas de exemplo e, depois, para a integração com o libvirt. Essa abordagem deve apresentar noções básicas suficientes do wxPython para você desenvolver um programa simples e, depois, expandir esse programa para adicionar recursos. Espero que você absorva esses conceitos e desenvolva-os a fim de atender às suas necessidades específicas.

Noções básicas de wxPython

Um bom local para começar é com algumas definições básicas. A biblioteca do wxPython é, na verdade, um wrapper sobre os wxWidgets baseados em C++. No contexto de criação de uma GUI, um widget é essencialmente um bloco de construção. Há cinco widgets independentes no nível superior da hierarquia de widgets:

wx.Frame,
wx.Dialog,
wx.PopupWindow,
wx.MDIParentFrame e
wx.MDIChildFrame.

A maioria dos exemplos aqui tem base no wx.Frame, pois, essencialmente, ele implementa uma única janela modal.

No wxPython, Frame é uma classe que você instancia como está, ou herda dela, a fim de adicionar ou aprimorar a funcionalidade. É importante entender como os widgets aparecem dentro de um quadro para que você saiba como colocá-los apropriadamente. O Layout é determinado pelo posicionamento absoluto ou usando dimensionadores. Um dimensionador é uma ferramenta útil que redimensiona os widgets quando o usuário muda o tamanho da janela clicando e arrastando um lado ou um canto.

A forma mais simples de um programa wxPython precisa ter algumas linhas de código para configurar tudo. Uma rotina típica principal pode parecer um pouco com a Listagem 1.


Listagem 1. Definição XML do dispositivo
	
if __name__ == "__main__":
    app = wx.App(False)
	frame = MyFrame()
	frame.Show()
	app.MainLoop()

Todo aplicativo wxPython é uma instância do wx.App() e precisa instanciá-lo como mostra a Listagem 1. Quando você passa False para wx.App, significa "não redirecione stdout e stderr para uma janela". A próxima linha cria um quadro instanciando a classe MyFrame() . Em seguida, você mostra o quadro e o controle de passagem para app.MainLoop(). A classe MyFrame() normalmente contém uma função __init__ para inicializar o quadro com seus widgets de preferência. Também é onde você conectaria quaisquer eventos de widget aos seus manipuladores apropriados.

Esse é provavelmente um bom local para mencionar uma ferramenta de depuração útil que acompanha o wxPython. Ela é chamada de ferramenta de inspeção de widget (consulte a Figura 1) e exige apenas duas linhas de código para ser usada. Primeiro, você precisa importá-la com:

import wx.lib.inspection

Em seguida, para usá-la, basta chamar a função Show() :

wx.lib.inspectin.InspectionTool().Show()

Clicar no ícone Events na barra de ferramentas do menu mostra dinamicamente os eventos, à medida que eles são acionados. É uma forma realmente interessante de ver eventos à medida que eles ocorrem, caso você não tenha certeza quais eventos um widget específico suporta. Também proporciona uma apreciação melhor do que acontece nos bastidores quando seu aplicativo está em execução.


Figura 1. A ferramenta de inspeção de widget do wxPython


Adicione uma GUI a uma ferramenta de linha de comando

A Parte 1 desta série apresentou uma ferramenta simples para exibir o status de todas as máquinas virtuais (VMs) em execução. É simples alterar essa ferramenta em uma ferramenta de GUI com wxPython. O widget wx.ListCtrl fornece somente a funcionalidade que você precisa para apresentar as informações de uma maneira tabular. Para usar um widget wx.ListCtrl , você precisa adicioná-lo ao seu quadro com a seguinte sintaxe:

self.list=wx.ListCtrl(frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER)

É possível escolher entre diversos estilos, incluindo as opções wx.LC_REPORT e wx.SUNKEN_BORDER usadas anteriormente. A primeira opção coloca o wx.ListCtrl no modo Report, que é um dos quatro modos disponíveis. Os outros são Icon, Small Icon e List. Para adicionar estilos como wx.SUNKEN_BORDER, basta usar o caractere de barra vertical (|). Alguns estilos são mutuamente exclusivos, como os estilos de borda diferente, portanto, consulte o wiki do wxPython se tiver dúvida (consulte Recursos).

Depois de instanciar o widget wx.ListCtrl , é possível começar a adicionar coisas a ele, como cabeçalhos de coluna. O método InsertColumn tem dois parâmetros obrigatórios e dois opcionais. Primeiro é o índice da coluna, que tem base em zero, seguido por uma cadeia de caracteres para definir o título. O terceiro serve para formatação e deve parecer com o seguinte LIST_FORMAT_CENTER, _LEFT ou _RIGHT. Finalmente, é possível definir uma largura fixa passando um número inteiro ou redimensionar automaticamente a coluna usando wx.LIST_AUTOSIZE.

Agora que você o widget wx.ListCtrl está configurado, use os métodos InsertStringItem e SetStringItem para preenchê-los com dados. Cada nova linha no widget wx.ListCtrl precisa ser adicionada usando o método InsertStringItem . Os dois parâmetros obrigatórios especificam onde realizar a inserção, com um valor de 0 aparecendo no topo da lista e a cadeia de caractere a ser inserida nesse local. InsertStringItem retorna um número inteiro indicando o número da linha da cadeia de caracteres inserida. É possível fazer uma chamada para GetItemCount() para a lista e usar o valor de retorno para o índice a fim de anexar na parte inferior, como mostra a Listagem 2 .


Listagem 2. Versão GUI da ferramenta de linha de comando
	
import wx
import libvirt

conn=libvirt.open("qemu:///system")

class MyApp(wx.App):
    def OnInit(self):
        frame = wx.Frame(None, -1, "KVM Info")
        id=wx.NewId()
        self.list=wx.ListCtrl(frame,id,style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.list.Show(True)
        self.list.InsertColumn(0,"ID")
        self.list.InsertColumn(1,"Name")
        self.list.InsertColumn(2,"State")
        self.list.InsertColumn(3,"Max Mem")
        self.list.InsertColumn(4,"# of vCPUs")
        self.list.InsertColumn(5,"CPU Time (ns)")

        for i,id in enumerate(conn.listDomainsID()):
            dom = conn.lookupByID(id)
            infos = dom.info()
            pos = self.list.InsertStringItem(i,str(id))
            self.list.SetStringItem(pos,1,dom.name())
            self.list.SetStringItem(pos,2,str(infos[0]))
            self.list.SetStringItem(pos,3,str(infos[1]))
            self.list.SetStringItem(pos,4,str(infos[3]))
            self.list.SetStringItem(pos,5,str(infos[2]))

        frame.Show(True)
        self.SetTopWindow(frame)
        return True

app = MyApp(0)
app.MainLoop()

A Figura 2 mostra os resultados desses esforços.


Figura 2. A ferramenta de informações GUI KVM

É possível aprimorar a aparência dessa tabela. Um aprimoramento considerável seria redimensionar as colunas. É possível fazer isso adicionando o parâmetro width = à chamada InsertColumn ou usar uma linha de código, como esta:

self.ListCtrl.SetColumnWidth(column,wx.LIST_AUTOSIZE)

A outra coisa que você pode fazer é adicionar um redimensionador de modo que os controles sejam redimensionados com a janela pai. É possível fazer isso com um wxBoxSizer em algumas linhas de código. Primeiro, você cria o redimensionador e, em seguida, adiciona a ele os widgets que você deseja redimensionar junto com a janela principal. Veja como seria o código:

self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.list, proportion=1,flag=wx.EXPAND | wx.ALL, border=5)
self.sizer.Add(self.button, flag=wx.EXPAND | wx.ALL, border=5)
self.panel.SetSizerAndFit(self.sizer)

A última chamada para self.panel.SetSizerAndFit() instrui o wxPython a definir o tamanho inicial do painel com base no tamanho mínimo do redimensionador dos widgets incorporados. Isso ajuda a dar à sua tela inicial um tamanho razoável com base no conteúdo interno.


Fluxo de controle baseado na ação de um usuário

Uma das coisas legais sobre o widget wx.ListCtrl é que é possível detectar quando um usuário clica em uma parte específica do widget e executa alguma ação com base nisso. Essa funcionalidade permite que você faça coisas como classificar uma coluna alfabeticamente, em ordem direta ou inversa, com base no clique do usuário no título da coluna. A técnica para conseguir isso usa um mecanismo de retorno de chamada. Você precisa fornecer uma função para lidar com cada ação que deseja processar, ligando o widget e o método de processamento. Faça isso com o método Bind .

Cada widget tem uma quantidade de eventos associados. Há também eventos associados a coisas como o mouse. Os eventos de mouse têm nomes como EVT_LEFT_DOWN, EVT_LEFT_UP e EVT_LEFT_DCLICK, junto com a mesma convenção de nomenclatura para os outros botões. É possível lidar com todos os eventos de mouse anexando ao tipo EVT_MOUSE_EVENTS . O truque é pegar o evento no contexto do aplicativo ou da janela na qual você está interessado.

Quando o controle passa para o manipulador de eventos, ele precisa executar as etapas necessárias para lidar com a ação e retornar o controle ao local anterior. Esse é o modelo de programação conduzido por evento que toda GUI precisa implementar para lidar com as ações do usuário de uma forma pontual. Muitos aplicativos GUI modernos implementam o multiencadeamento para não dar ao usuário a impressão de que o programa não está respondendo. Eu falo brevemente sobre isso posteriormente neste artigo.

Timers representam outro tipo de evento com os quais um programa precisa lidar possivelmente. Por exemplo, convém executar uma função de monitoramento periódico a um intervalo definido pelo usuário. Seria necessário fornecer uma tela na qual o usuário especificaria o intervalo e iniciar um timer que acionaria um evento ao expirar. A expiração do timer aciona um evento que pode ser usado para ativar uma seção de código. Talvez seja necessário definir ou reiniciar o tempo, dependendo novamente da preferência do usuário. Você poderia usar facilmente essa técnica para desenvolver uma ferramenta de monitoramento de VM.

A Listagem 3 fornece um aplicativo de demonstração simples com linhas de um botão e um texto estático. Usar wx.StaticText é uma maneira fácil de mostrar uma cadeia de caracteres na janela. A ideia é clicar no botão uma vez para iniciar um timer e registrar o horário de início. Clicar no botão registra o horário de início e muda o rótulo para Stop. Clicar no botão novamente preenche a caixa de texto do horário de término e muda o botão de volta para Start.


Listagem 3. Aplicativo simples com um botão e um texto estático
	
import wx
from time import gmtime, strftime

class MyForm(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Buttons")
        self.panel = wx.Panel(self, wx.ID_ANY)

        self.button = wx.Button(self.panel, id=wx.ID_ANY, label="Start")
        self.button.Bind(wx.EVT_BUTTON, self.onButton)

    def onButton(self, event):
        if self.button.GetLabel() == "Start":
            self.button.SetLabel("Stop")
            strtime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
            wx.StaticText(self, -1, 'Start Time = ' + strtime, (25, 75))
        else:
            self.button.SetLabel("Start")
            stptime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
            wx.StaticText(self, -1, 'Stop Time = ' + stptime, (25, 100))

if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm()
    frame.Show()
    app.MainLoop()


GUI de monitoramento aprimorado

Agora, é possível adicionar a funcionalidade à GUI de monitoramento simples apresentada anteriormente. Há mais uma parte do wxPython que você precisa entender antes de ter tudo que precisa para criar seu aplicativo. A adição de uma caixa de seleção à primeira coluna de um widget wx.ListCtrl possibilitaria a execução de uma ação em diversas linhas com base no status da caixa de seleção. É possível fazer isso usando o que o wxPython chama de mixins. Essencialmente, um mixin é uma classe auxiliar que adiciona algum tipo de funcionalidade ao widget pai. Para adicionar caixa de seleção mixin, basta usar o código a seguir para instanciá-la:

listmix.CheckListCtrlMixin.__init__(self)		

Também é possível aproveitar a vantagem dos eventos para adicionar a capacidade de marcar ou limpar todas as caixas clicando no título da coluna. Fazer isso simplifica a execução de coisas como iniciar ou parar todas as VMs com apenas alguns cliques. É necessário escrever alguns manipuladores de evento a fim de responder aos eventos apropriados da mesma forma que você mudou o rótulo no botão anteriormente. Esta é a linha de código para configurar um manipulador para o evento de clique na coluna:

self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)

wx.EVT_LIST_COL_CLICK é acionado quando qualquer cabeçalho de coluna é clicado. Para determinar qual coluna foi clicada, use o método event.GetColumn() . Veja uma função de manipulador simples para o evento OnColClick :

def OnColClick(self, event):
    print "column clicked %d\n" % event.GetColumn()
	event.Skip()

A chamada de event.Skip() é importante se você precisar propagar o evento para outros manipuladores. Embora essa necessidade não seja aparente nessa instância, ela pode se tornar um problema quando diversos manipuladores precisarem processar o mesmo evento. Há uma boa discussão sobre propagação de eventos no site de wiki do wxPython, com muito mais detalhes do que este artigo permite.

Finalmente, adicione o código aos dois manipuladores de botão para iniciar ou parar todas as VMs marcadas. É possível iterar sobre as linhas em seu wx.ListCtrl e obter o ID da VM com apenas algumas linhas de código, como mostra a Listagem 4 .


Listagem 4. Iniciando e parando as VMs marcadas
	
#!/usr/bin/env python

import wx
import wx.lib.mixins.listctrl as listmix
import libvirt

conn=libvirt.open("qemu:///system")

class CheckListCtrl(wx.ListCtrl, listmix.CheckListCtrlMixin,
                                 listmix.ListCtrlAutoWidthMixin):
    def __init__(self, *args, **kwargs):
        wx.ListCtrl.__init__(self, *args, **kwargs)
        listmix.CheckListCtrlMixin.__init__(self)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        self.setResizeColumn(2)

class MainWindow(wx.Frame):

    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self)
        self.list = CheckListCtrl(self.panel, style=wx.LC_REPORT)
        self.list.InsertColumn(0, "Check", width = 175)
        self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick, self.list)

        self.list.InsertColumn(1,"Max Mem", width = 100)
        self.list.InsertColumn(2,"# of vCPUs", width = 100)

        for i,id in enumerate(conn.listDefinedDomains()):
            dom = conn.lookupByName(id)
            infos = dom.info()
            pos = self.list.InsertStringItem(1,dom.name())
            self.list.SetStringItem(pos,1,str(infos[1]))
            self.list.SetStringItem(pos,2,str(infos[3]))

        self.StrButton = wx.Button(self.panel, label="Start")
        self.Bind(wx.EVT_BUTTON, self.onStrButton, self.StrButton)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.list, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        self.sizer.Add(self.StrButton, flag=wx.EXPAND | wx.ALL, border=5)
        self.panel.SetSizerAndFit(self.sizer)
        self.Show()

    def onStrButton(self, event):
        if self.StrButton.GetLabel() == "Start":
	    num = self.list.GetItemCount()
            for i in range(num):
                if self.list.IsChecked(i):
                    dom = conn.lookupByName(self.list.GetItem(i, 0).Text)
                    dom.create()
                    print "%d started" % dom.ID()

    def OnColClick(self, event):
         item = self.list.GetColumn(0)
         if item is not None:
             if item.GetText() == "Check":
                 item.SetText("Uncheck")
                 self.list.SetColumn(0, item)
                 num = self.list.GetItemCount()
                 for i in range(num):
                     self.list.CheckItem(i,True)
             else:
                 item.SetText("Check")
                 self.list.SetColumn(0, item)
                 num = self.list.GetItemCount()
                 for i in range(num):
                     self.list.CheckItem(i,False)

         event.Skip()

app = wx.App(False)
win = MainWindow(None)
app.MainLoop()


Há duas coisas a serem apontadas aqui com relação ao estado das VMs no KVM: as VMs em execução são exibidas quando você usa o método listDomainsID() do libvirt. Para ver as máquinas que não estão em execução, você precisa usar listDefinedDomains(). É necessário apenas manter esses dois separados de modo que você saiba quais VMs você pode iniciar e quais você pode parar.


Concluindo

Este artigo se concentrou principalmente nas etapas necessárias para o desenvolvimento de um wrapper de GUI usando wxPython que, por sua vez, gerenciar o KVM com libvirt. A biblioteca wxPython é ampla e fornece uma grande variedade de widgets a fim de permitir que você desenvolva aplicativos baseados em GUI com uma aparência profissional. Este artigo apenas deu uma ideia dos recursos possíveis, mas espero que o tenha motivado a investigar ainda mais. Consulte mais Recursos para ajudá-lo a executar seu aplicativo.


Recursos

Aprender

Obter produtos e tecnologias

  • Avalie produtos de software IBM: a partir de downloads de teste para produtos hospedados na nuvem, é possível inovar no seu próximo projeto de desenvolvimento de software livre usando software especialmente para desenvolvedores.

Discutir

Sobre o autor

Paul Ferrill escreve nos meios de comunicação de comércio de computadores há mais de 20 anos. Começou escrevendo resenhas de rede para o PC Magazine sobre produtos como LANtastic e versões anteriores do Novell Netware. Paul é graduado e possui mestrado em engenharia elétrica e escreveu softwares para mais arquiteturas e plataformas de computador do que consegue se lembrar.

Ajuda para Relatar Abuso

Relatar abuso

Obrigado. Esta entrada foi sinalizada para atenção do moderador.


Ajuda para Relatar Abuso

Relatar abuso

Falha no envio do Relatório de abuso. Tente novamente mais tarde.


developerWorks: Registre-se


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Na primeira vez que você efetua sign in no developerWorks, um perfil é criado para você. Informações selecionadas do seu perfil developerWorks são exibidas ao público, mas você pode editá-las a qualquer momento. Seu primeiro nome, sobrenome (a menos que escolha ocultá-los), e seu nome de exibição acompanharão o conteúdo que postar.

Selecione seu nome de exibição

Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

(Deve possuir de 3 a 31 caracteres.)


Ao clicar em Enviar, você concorda com os termos de uso do developerWorks.

 


Classificar este artigo

Comentários

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux, Software livre
ArticleID=791998
ArticleTitle=Gerando script KVM com Python, Parte 2: Adicione uma GUI para gerenciar KVM com libvirt e Python
publish-date=02102012

Conheça a IBM da sua cidade

Virtual Branch Office Brasil

A IBM está mais perto do que você imagina!


Tags

Help
Use o campo de pesquisa para encontrar todos os tipos de conteúdo no My developerWorks com essa tag.

Use a barra de rolagem para ver mais ou menos tags.

Tags populares mostra as principais tags para esta zona de conteúdo em particular (por exemplo, Java technology, Linux, WebSphere).

Minhas tags mostra suas tags para esta zona de conteúdo em particular (por exemplo, Java technology, Linux, WebSphere).

Use o campo de pesquisa para localizar todos os tipos de conteúdo no Meu developerWorks com essa tag. Tags populares mostra as tags principais para essa zona de conteúdo particular (por exemplo, tecnologia Java, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere). Minhas tags mostra as suas tags para essa zona de conteúdo em particular (por exemplo, tecnologia Java, Linux, WebSphere).