quarta-feira, 19 de setembro de 2012

Interface Gráfica no Python com PyQt

Como Separar a Interface Gráfica da Aplicação em Si 

 

Quem aprende a programar em Python e não tem foco na web, logo começa a fazer projetos de programas com interface gráfica. E o PyQt é uma das melhores opções disponíveis para criar programas fora da linha de comando. Sobretudo quando decidimos aprender por conta própria, os primeiros projetos com interface gráfica podem sair um pouco confusos.
O objetivo deste artigo é dar uma dica valiosa para aqueles que estão iniciando por este caminho.
A dica é manter a interface gráfica separada do seu programa.
É bastante comum ver tutoriais de programação para iniciantes na internet onde os exemplos, para serem sucintos, integram a interface gráfica e o código do programa. E às vezes o programador iniciante demora para perceber que existem outras formas de se fazer a mesma coisa.
Existem duas grandes vantagens de se manter o código da interface gráfica separado do código da aplicação:

1 - É uma questão de filosofia. A interface gráfica não é o seu programa. Ela é um conjunto de componentes basicamente estáticos que não vão agregar em nada no seu trabalho se misturados com as linhas de código que realmente fazem o programa acontecer. Qualquer interface gráfica do PyQt com 5 ou 10 widgets já gera um código de 50 a 70 linhas. E uma vez que você definiu o modelo da sua GUI, são 50 a 70 linhas que você precisa pular toda vez que for trabalhar no seu código. Por isso, é muito mais simples pensar na GUI como um componente do seu programa, ficando em seu arquivo separado e sendo chamado quando necessário.
2 - É uma questão prática. Tenha a certeza de que, a menos que o seu projeto seja extremamente simples e pequeno, você não vai acertar a interface gráfica do programa de primeira. Você vai criar o modelo e gerar o código Python. Depois você vai desenvolvendo sua aplicação, fazendo testes e em determinado momento vai perceber que precisará de outro botão, outra caixa de texto, outra aba, ou até reposicionar completamente os widgets da GUI. Se você tiver adotado a tática do super módulo, com o código da interface dentro do seu programa, você tem duas opções. Pode abrir o modelo no Qt Designer, alterar a GUI, gerar o código Python e aí copiar e colar as modificações, linha por linha, no seu programa; OU você pode fazer as alterações na unha mesmo, diretamente no código, sem usar o Designer.

Agora, se você preferir manter o código da interface gráfica em um módulo separado do Python, além da organização, outra vantagem é que quando você tiver necessidade de modificar sua GUI o código gerado já está pronto para ser integrado no programa.
Vou mostrar alguns exemplos.
Criei este modelo de GUI simples no Qt Designer chamado teste.ui:
Agora vou gerar o código Python deste modelo com o pyuic. No prompt de comandos do Windows uma forma de fazer é:
cd c:\python32\Lib\site-packages\PyQt4\uic
c:\python32\Lib\site-packages\PyQt4\uic\pyuic.py c:\meu_projeto\teste.ui -o c:\meu_projeto\minha_ui.py

O código gerado no arquivo minha_ui.py é o seguinte:
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'c:\python32\blog\teste.ui'
#
# Created: Wed Sep 19 14:30:42 2012
#      by: PyQt4 UI code generator 4.9.4
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(657, 272)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.pushButton = QtGui.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(270, 170, 75, 23))
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.lineEdit = QtGui.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(70, 110, 541, 20))
        self.lineEdit.setAlignment(QtCore.Qt.AlignCenter)
        self.lineEdit.setObjectName(_fromUtf8("lineEdit"))
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 657, 21))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton.setText(QtGui.QApplication.translate("MainWindow", "Pressione", None, QtGui.QApplication.UnicodeUTF8))

É bastante comum ver programas sendo construídos em cima deste arquivo. No entanto, outra forma de fazer é manter este arquivo intacto, observando apenas os nomes das classes e funções, que são geradas automaticamente pelo PyQt mas que podem variar de acordo com o tipo de GUI que escolhemos (neste exemplo, optei pelo Main Window).
O que vamos fazer agora é criar uma subclasse que possa herdar os métodos e atributos definidos acima.
Veja no exemplo do arquivo meu_programa.py criado no mesmo diretório do módulo minha_ui.py:
# -*- coding: utf-8 -*-

from PyQt4 import QtCore, QtGui
import sys
import minha_ui

class Main(QtGui.QMainWindow, minha_ui.Ui_MainWindow):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.setupUi(self)  
        # aqui voce faz os bindings com funcoes do python
        self.connect(self.pushButton, 
                     QtCore.SIGNAL("clicked()"),
                     self.testa)

    def testa(self):
        self.lineEdit.insert("Funciona!")

def main():    
    app = QtGui.QApplication(sys.argv)
    main_window = Main()
    main_window.show()
    app.exec_()

if __name__ == '__main__':
    main()

Este módulo agora é o módulo principal do programa. Nele podemos definir as funções do programa ou ainda importar de um terceiro arquivo. Ao ser executado, o resultado é este:
Agora, se eu precisar adicionar algum elemento novo na minha interface gráfica, ou melhorar este alinhamento medíocre, basta abrir o Qt Designer, fazer as modificações, gerar o novo código Python que irá substituir o módulo atual e automaticamente as alterações já estarão valendo.

2 comentários:

  1. como sugestão de artigo, um crud em pyqt seria interessante.

    ResponderExcluir