wxPython: Tudo sobre menus

Se você não tem nada a ver com programação, nem tem interesse no assunto, simplesmente ignore este artigo.


Mike Driscoll tem um blog dedicado a Python chamado The Mouse vs. the Python e eventualmente há excelentes artigos por lá.

Esta é uma tradução livre do artigo wxPython: All about menus e modifiquei sutilmente algumas coisas, incluindo os exemplos. Agradeço ao Mike pelos excelentes artigos e me desculpo pelas liberdades que tomei na tradução (incluindo a mudança no nome do artigo). Vamos a ele!


Menus são onipresentes. Eles estão praticamente em quase os programas para desktop. Você os usa para editar preferências ou configurar seu programa. Em wxPython, há várias opções de menu para se escolher. A mais familiar é provavelmente wx.Menu. Mas há menus popup e uma implementação própria do Python conhecida como FlatMenu. Nós cobriremo aqui o wx.Menu e os menus popup porque ambos estão relacionados. FlatMenu inclui a API toolbar, assim você vai ter que esperar por um outro artigo que trate exclusivamente desses widgets. Então vamos começar a festa dos menus!

Um Exemplo Simples de Menu

Um exemplo simples de menu

Vamos começar com algo realmente simples. Um menu que só tem uma opção: Sair. Veja o código:

import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        menuBar = wx.MenuBar()
        fileMenu = wx.Menu()
        exitMenuItem = fileMenu.Append(wx.NewId(), "Sair",
                                       "Sair do programa")
        menuBar.Append(fileMenu, "&Arquivo")
        self.Bind(wx.EVT_MENU, self.onExit, exitMenuItem)
        self.SetMenuBar(menuBar)
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """"""
        self.Close()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Vamos detalhar melhor isso mais adiante. Para criarmos a barra de menu, instanciamos wx.MenuBar. Então criamos uma instância de wx.Menu, che chamamos de filemenu. Finalmente, adicionamos o item "Sair". Em essência, nós empilhamos wx.MenuItem, mas isso foi um tipo de atalho, já que não chegamos a instanciar um wx.MenuItem primeiro. Nós mostraremos como fazer isso no nosso próximo exemplo. Note que ao adicionarmos um item, temos que passar um id, uma string de etiqueta e uma string de estado. Esta última será mostrada quando você posicionar o mouse sobre o item de menu, desde que você tenha uma barra de estado. Not que para anexar um manipulador de eventos ao item de menu, você precisa usar o evento EVT_MENU e vinculá-lo ao frame. A seguir adicione o Menu propriamente ao objeto MenuBar e passe uma string também, que é neste caso "Arquivo". Finalmente, chamamos o método SetMenuBar do frame para anexar a barra de menu ao frame.

Adicionando uma imagem ao menu

Isso é tudo o que é preciso para criarmos um menu! Agora vamos mostrar um exemplo mais complex! Nota: para seguir o exemplo abaixo, você precisará usar seu próprio arquivo de imagem.

wx.Menu Imagem

# coding: utf-8
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        # create the menubar
        menuBar = wx.MenuBar()
 
        # create the first menu (starting on left)
        carMenu = wx.Menu()
        carMenu.Append(101, "&Ford", "Fabricante estadunidense")
        carMenu.Append(102, "&Nissan", "")
        carMenu.Append(103, "&Toyota", "Japoneses!")
        carMenu.Append(104, "&Sair", "Fechar a aplicação")
 
        # add a picture to a menu
        picMenu = wx.Menu()
        item = wx.MenuItem(picMenu, wx.ID_ANY, "Cobra", "Este menu tem uma imagem!")
        img = wx.Image('snake32.png', wx.BITMAP_TYPE_ANY)
        item.SetBitmap(wx.BitmapFromImage(img))
        picMenu.AppendItem(item)
 
        # add menus to menubar
        menuBar.Append(carMenu, "&Veículos")
        menuBar.Append(picMenu, "&Imagens")
        self.SetMenuBar(menuBar)
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """"""
        self.Close()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Este exemplo é similar ao primeiro. A principal diferença é que nós adicionamos itens no lugar do menu File. Note que desta vez, especificamos nossos números ID explicitamente. Isso não é geralmente recomendado porque você pode terminar substituindo algum ID requerido pelo wx. Porém, você deve ter visto exemplos assim pela Internet. A próxima grande diferença não vem antes do picMenu. Aqui nós criamos um wx.MenuItem e adicionamos uma imagem a ele via wx.Image e o método SetBitmap do MenuItem. O resto é basicamente o mesmo.

Agora vamos investir algum tempo vendo como adicionar botões de marcar e selecionar (check e radio, respectivamente) ao nosso menu.

Adicionando Botões de Marcar e de Selecionar (Radio)

Tela demonstrando uso de radio e check boxes em menus com wxPython

Adicionar um botão de marcar ou selecionar ao seu menu é bastante simples. Vamos dar uma olhada em como isso é feito!

# coding: utf-8
# radiocheck.py
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        # Create menu bar
        menuBar = wx.MenuBar()
 
        # Create radio menu
        radioMenu = wx.Menu()
        idleItem = radioMenu.Append(wx.NewId(), "IDLE",
                                   "um shell para Python feito em Tcl/Tk",
                                   wx.ITEM_RADIO)
        pyCrustItem = radioMenu.Append(wx.NewId(),"PyCrust",
                                      "um shell para Python feito em wxPython",
                                      wx.ITEM_RADIO)
        psiItem = radioMenu.Append(wx.NewId(), "psi",
                                  "um shell simples para Python feito em wxPython",
                                  wx.ITEM_RADIO)
        menuBar.Append(radioMenu, "&Radio")
 
        # create check menu
        checkMenu = wx.Menu()
        wgItem = checkMenu.Append(wx.NewId(), "Wells Fargo", "", wx.ITEM_CHECK)
        citiItem = checkMenu.Append(wx.NewId(), "Citibank", "", wx.ITEM_CHECK)
        geItem = checkMenu.Append(wx.NewId(), "GE Money Bank", "", wx.ITEM_CHECK)
        menuBar.Append(checkMenu, "&Check")
 
        # Attach menu bar to frame
        self.SetMenuBar(menuBar)
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Sim, como você pode ver, tudo o que você precisa fazer é adicionar uma flag wx.ITEM_RADIO ou wx.ITEM_CHECK como um tipo de parâmetro, que é o quarto parâmetro. Por que é chamado de "tipo" ao invés de "estilo" como nos outros componentes visuais? Bem, enquanto discutia sobre isso no canal de IRC do wxPython, Robin Dunn (criador do wxPython) disse que provavelmente é assim por se tratarem de tipos de itens de menu diferentes.

Sub-Menus

Tela de exemplo do uso de submenus

A biblioteca wxPython suporta sub-menus. Aqui está um exemplo realmente simples que nos mostra como se faz iso.

# coding: utf-8
# submenu.py
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial wx.Menu")
 
        self.panel = wx.Panel(self, wx.ID_ANY)
 
        menuBar = wx.MenuBar()
        fileMenu = wx.Menu()
        openMenuItem = fileMenu.Append(wx.NewId(), "Abrir")
 
        # create a submenu
        subMenu = wx.Menu()
        historyMenuItem = subMenu.Append(wx.NewId(), "Mostrar Histórico")
        fileMenu.AppendMenu(wx.NewId(), "Histórico", subMenu)
 
        exitMenuItem = fileMenu.Append(wx.NewId(), "Sair",
                                       "Deixar a aplicação")
        menuBar.Append(fileMenu, "&Arquivo")
        self.Bind(wx.EVT_MENU, self.onExit, exitMenuItem)
        self.SetMenuBar(menuBar)
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """"""
        self.Close()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

O segredo aqui é que ao invés de usarmos o método Append do filemenu, usamos o método AppendMenu. Como o novo sugere, ele permite ao programador acrescentar um menu ao invés de um item de menu. Yup, é isso aí!

Menus Flutuantes (ou Pop-up ou ContextMenus)

Tela de exemplo de uso dos menus flutuantes

Menus flutuantes são menus geralmente acessados através do botão direito do mouse em um navegador ou em um arquivo. Eles também são conhecidos como Menus de Contexto (ou menus pop-up). Aqui temos um exemplo bastante trivial para você estudar.

# coding: utf-8
# submenu.py
import wx
 
########################################################################
class MyForm(wx.Frame):
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Tutorial de Menu Flutuante")
 
        panel = wx.Panel(self, wx.ID_ANY)
 
        lbl = wx.StaticText(panel, label="Clique com o botão direito em qualquer lugar por aqui!")
        self.Bind(wx.EVT_CONTEXT_MENU, self.onContext)
 
    #----------------------------------------------------------------------
    def onContext(self, event):
        """
        Create and show a Context Menu
        """
 
        # only do this part the first time so the events are only bound once 
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()
            self.itemTwoId = wx.NewId()
            self.itemThreeId = wx.NewId()
            self.Bind(wx.EVT_MENU, self.onPopup, id=self.popupID1)
            self.Bind(wx.EVT_MENU, self.onPopup, id=self.itemTwoId)
            self.Bind(wx.EVT_MENU, self.onExit, id=self.itemThreeId)
 
        # build the menu
        menu = wx.Menu()
        itemOne = menu.Append(self.popupID1, "ItemUm")
        itemTwo = menu.Append(self.itemTwoId, "ItemDois")
        itemThree = menu.Append(self.itemThreeId, "Sair")
 
        # show the popup menu
        self.PopupMenu(menu)
        menu.Destroy()
 
    #----------------------------------------------------------------------
    def onExit(self, event):
        """
        Exit program
        """
        self.Close()
 
    #----------------------------------------------------------------------
    def onPopup(self, event):
        """
        Print the label of the menu item selected
        """
        itemId = event.GetId()
        menu = event.GetEventObject()
        menuItem = menu.FindItemById(itemId)
        print menuItem.GetLabel()
 
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm().Show()
    app.MainLoop()

Para começar, ligamos wx.EVT_CONTEXT_MENU ao frame. Isso nos permite clicar com o botão direito em qualquer lugar e acionar o evento de menu de contexto, que criará e mostrará o menu flutuante. O código no método onContext é baseado no demo do wxPython para menus flutuantes. Como você pode ver, usamos uma checagem condicional, testando s o evento de menu já foi ativado. Se ele já foi ativado, não o será novamente. Em seguida, criamos nosso menu de um modo bem parecido com os anteriores. Finalmente, chamamos o método PopupMenu do frame e passamos para ele o nosso novo menu. Ele então mostra o menu ao usuário. Quando o usuário clica em um item do menu, o evento associado ao item será ativado e o menu será destruído.

Os primeiros dois itens do menu estão ligados ao método onPopup. Isso nos permite ver como podemos acessar o Menu e os atributos de um MenuItem. Você pode obter o id do menu com o evento e do Menu em si com o método GetEventObject do evento. Assim você pode usar o método FindItemById do menu para pegar um controlador para o item de menu propriamente. Finalmente, imprimimos a etiqueta do item de menu.

E pra terminar

Now you should know most of the menu methods and how to create them, bind events and make different kinds of menu items. You even know how to create popup menus! Now you can make your applications have fancy menus too.

Agora você já conhece grande parte dos métodos de menu e como criar menus, controlar eventos e fazer diferentes tipos de itens de menu. Você também sabe como criar menus flutuantes! Agora você pode fazer suas aplicações também terem menus originais.

Foto do post: Black Rat Snake, de cotinis.

Avalie: 
Average: 3.4 (248 votes)

Comentários

imagem de Jose Ricardo
Enviado por Jose Ricardo (não verificado) em 18. Março 2014 - 17:30

Meu caro,

Parabéns pelo post. Está bem legal. Mas faltam as imagens de como ficam suas contruções em wxpython, bem como os efeitos das alterações realizadas.

Abraço,

Comentar


Warning: PHP Startup: Unable to load dynamic library '/opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo.so' - /opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo.so: cannot open shared object file: No such file or directory in Unknown on line 0

Warning: PHP Startup: Unable to load dynamic library '/opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo_mysql.so' - /opt/php56/lib/php/extensions/no-debug-non-zts-20131226/pdo_mysql.so: cannot open shared object file: No such file or directory in Unknown on line 0

Warning: PHP Startup: Unable to load dynamic library '/opt/php56/lib/php/extensions/no-debug-non-zts-20131226/php_pdo_odbc.dll' - /opt/php56/lib/php/extensions/no-debug-non-zts-20131226/php_pdo_odbc.dll: cannot open shared object file: No such file or directory in Unknown on line 0