associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Diferenças para "TutorialPyQt"

Diferenças entre as versões de 2 e 10 (8 versões de distância)
Revisão 2e 2004-03-17 16:27:28
Tamanho: 13195
Comentário:
Revisão 10e 2008-09-26 14:06:26
Tamanho: 14091
Editor: localhost
Comentário: converted to 1.6 markup
Deleções são marcadas assim. Adições são marcadas assim.
Linha 3: Linha 3:
Este é um pequeno tutorial para iniciantes em PyQt. Assume-se que o leitor possua algum conhecimento prévio de bash, Python e Qt.
Este é um pequeno tutorial para iniciantes em PyQt. Assume-se que o leitor possua algum conhecimento prévio de Bash, Python e Qt.
Linha 9: Linha 8:
  * Como utilizar o Qt Designer para gerar arquivos ui para Qt.
  * Como usar a ferramenta pyuic para gerar programas python.

* Como utilizar o Qt Designer para gerar arquivos {{{.ui}}} para Qt.
  * Como usar a ferramenta {{{pyuic}}} para gerar programas Python.
Linha 12: Linha 12:
  * Finalmente, criaremos uma aplicação simples para comunicar com o comando 'at'.   * Finalmente, criaremos uma aplicação simples para comunicar com o comando {{{'at'}}}.
Linha 18: Linha 18:
  * qt-devel RPM instalado
  * !PyQt-devel RPM instalado
     * qt-devel RPM instalado
     * !PyQt-devel RPM instalado

''Não precisa ser RPM, desde que você instale qt-devel e !PyQt-devel. Essas bibliotecas já vêm na maioria das distribuições, como Mandrake, Suse, Fedora, etc. -- RodrigoVieira''
Linha 23: Linha 25:
Para seguir esse tutorial, você deverá saber: Para seguir esse tutorial, você deverá saber:
Linha 26: Linha 29:
  * Alguns comandos bash básicos.
  * O básico de programação em Qt.

Se você não possui todos os conhecimentos acima, poderá experimentar alguns problemas em fazer este tutorial funcionar.
  * Alguns comandos Bash básicos.
  * Conhecimento básico de programação em Qt.

Se você não possui todos os conhecimentos acima, poderá ter alguns problemas para seguir este tutorial.
Linha 33: Linha 36:
Primeiramente, vamos começar onde eu começo. Abra uma janela de comando bash. Inicie o Qt Designer digitando o seguinte comando:

  {{{
Primeiramente, vamos começar onde eu começo. Abra uma janela de comando Bash e inicie o Qt Designer digitando o seguinte comando:

{{{
Linha 39: Linha 42:
Eu não vou assumir que você seja totalmente iniciante na utilização do Qt Designer. Caso seja, você pode facilmente consultar a documentação.

Crie uma widget nova. Nomeie-a 'at_auto'. Adicione algumas coisas nela:
Eu vou assumir que você não seja totalmente iniciante na utilização do Qt Designer. Caso seja, você pode facilmente consultar a documentação.

Crie uma widget nova. Nomeie-a '{{{at_auto}}}'. Adicione algumas coisas nela:
Linha 49: Linha 52:
Salve o arquivo em um diretório para esse tutorial. Se você ainda não o criou, crie um chamado "pyqt_tutorial" ou algo assim. Salve o arquivo como "at.ui". Salve o arquivo em um diretório para esse tutorial. Se você ainda não o criou, crie um chamado "{{{pyqt_tutorial}}}" ou algo assim. Salve o arquivo como "{{{at.ui}}}".
Linha 53: Linha 56:
Volte ao prompt do bash, ou abra uma nova janela de comando. Vá ao diretório que você acabou de criar, e execute o seguinte comando.

  {{{
Volte ao prompt do Bash, ou abra uma nova janela de comando. Vá ao diretório que você acabou de criar, e execute o seguinte comando.

{{{
Linha 59: Linha 62:
Esse comando irá imprimir em stdout o arquivo python gerado a partir do arquivo .ui do Qt. Nós queremos salvar essa saída, então nós rodaremos o seguinte comando.

  {{{
Esse comando irá imprimir em stdout o arquivo Python gerado a partir do arquivo {{{.ui}}} do Qt. Nós queremos salvar essa saída, então nós rodaremos o seguinte comando.

{{{
Linha 65: Linha 68:
----
  
'Petr Vanek (subzero AT py DOT cz)':
    '''CUIDADO:''' usando
pyuic dessa forma ''pode'' ser errado. Pyuic escreve algumas informações na saída-padrão que não é código python '''de verdade'''!

   
Use-o dessa forma:
    {{{
''Usar o {{{pyuic}}} dessa forma pode ser errado. {{{pyuic}}} escreve algumas informações na saída-padrão que não são código Python "de verdade"! Use-o dessa forma:
{{{
Linha 73: Linha 72:

----

  
Este problema não afeta o pyuic que vem com !PyQt 3.5 (que é o que !RedHat 8 e 9 usam).

 
Eu postei um patch na lista PyKDE para consertar isso. Phil a aceitou. Deve aparecer na próxima versão (3.7?)

 
-- Jonathan Gardner

----


Nós depositamos a saída do comando em at_auto.py. Toda vez que alterarmos o arquivo at.ui, precisaremos regenerar o arquivo at_auto.py. Vamos adicionar esse comando a um makefile.

/!\ Tabs são importantes!

  {{{
$ cat >
Makefile
at_auto.py : at.ui
''
''
-- Petr Vanek (subzero AT py DOT cz)

Este problema não afeta o {{{pyuic}}} que vem com PyQt 3.5 (que é o que !RedHat 8 e 9 usam). Eu postei um patch na lista PyKDE para consertar isso. Phil a aceitou. Deve aparecer na próxima versão (3.7?) -- Jonathan Gardner''

Nós depositamos a saída do comando em {{{at_auto.py}}}. Toda vez que alterarmos o arquivo {{{at.ui}}}, precisaremos regerar o arquivo {{{at_auto.py}}}. Vamos adicionar esse comando a um {{{Makefile}}}.

{{{
# Arquivo:
Makefile
at_auto.py: at.ui
Linha 92: Linha 83:
^D
}}}

----
  '
''Obs:''' Se você está usando !PyQt 3.6, você terá que usar o método '-o' descrito por Petr acima. Você pode querer usar de qualquer forma, por outros motivos vários.

  {{{
$ cat >
Makefile
}}}

/!\ Note que nós usamos Tab no arquivo {{{Makefile}}}.

''Obs: Se você está usando PyQt 3.6, você terá que usar o método '-o' descrito por Petr acima. Você pode querer usar de qualquer forma, por outro motivo:''

{{{
# Arquivo:
Makefile
Linha 102: Linha 93:
^D
}}}
----


Agora execute o makefile.

    {{{
}}}

Agora execute o {{{Makefile}}}.

{{{
Linha 112: Linha 101:
Note que ele diz algo sobre todos os arquivos estarem atualizados. Vamos executar um touch em at.ui para que ele conste como mais recente que at_auto.py, e então executar make de novo.

  {{{
Note que ele diz algo sobre todos os arquivos estarem atualizados. Vamos executar um {{{touch}}} em {{{at.ui}}} para que ele conste como mais recente que {{{at_auto.py}}}, e então executar {{{make}}} novamente.

{{{
Linha 119: Linha 108:
Agora ele ecoou os comandos executados. Veja que ele regenerou at_auto.py com sucesso. Agora ele ecoou os comandos executados. Veja que ele regenerou {{{at_auto.py}}} com sucesso.
Linha 123: Linha 112:
A idéia aqui é que você quer que o desenvolvedor GUI seja capaz de fazer alterações na interface (como mudar a posição de objetos) sem afetar a lógica por trás da GUI. Então com a configuração que temos agora, tudo o que o desenvolvedor GUI tem que fazer é usar Qt Designer para mudar o arquivo .ui, e então rodar o comando make para ver suas alterações serem efetuadas.

Seu arquivo make ficará mais complicado à medida que você adiciona mais arquivos. Mas não esqueça de ler mais sobre make para que você possa saber de antemão como utilizá-lo apropriadamente.
A idéia aqui é que você quer que o desenvolvedor GUI seja capaz de fazer alterações na interface (como mudar a posição de objetos) sem afetar a lógica por trás da GUI. Então com a configuração que temos agora, tudo o que o desenvolvedor GUI tem que fazer é usar Qt Designer para mudar o arquivo {{{.ui}}}, e então rodar o comando {{{make}}} para ver suas alterações serem efetuadas.

Seu arquivo {{{make}}} ficará mais complicado à medida que você adiciona mais arquivos. Mas não esqueça de ler mais sobre {{{make}}} para que você possa saber de antemão como utilizá-lo apropriadamente.
Linha 129: Linha 118:
Então nós temos aquele arquivo .ui, e também o arquivo at_auto.py. Como nós rodamos o programa então?

Nós temos que criar at.py. Aqui está como ele parecerá.

  {{{
Então nós temos aquele arquivo {{{.ui}}}, e também o arquivo {{{at_auto.py}}}. E agora?

Nós temos que criar {{{at.py}}}, o programa principal. Aqui está o código:

{{{
Linha 152: Linha 141:
Now, run it.

  {{{
Agora é só executá-lo.

{{{
Linha 160: Linha 149:
NOTA:
Se você está usando Qt Designer com a versão 3.3.0 do Qt, seu arquivo .ui contém
<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
no cabeçalho, e pyuic (3.8.1 pelo menos) reclamará que essa versão é muito recente e não produzirá nenhuma saída.

Para resolver isso da forma fácil, você pode criar um pequeno script que irá corrigir esse erro automaticamente,
execute make e rode seu novo programa:
NOTA: Se você está usando Qt Designer com a versão 3.3.0 do Qt, seu arquivo {{{.ui}}} contém:

{{{

<!DOCTYPE UI>
<UI version="3.3" stdsetdef="1">
}}}

no cabeçalho, e {{{pyuic}}} (3.8.1 pelo menos) reclamará que essa versão é muito recente e não produzirá nenhuma saída.

Para resolver isso da forma fácil, você pode criar um pequeno script que irá corrigir esse erro automaticamente, execute {{{make}}} e rode seu novo programa:

{{{
Linha 169: Linha 162:

sed -i s/3.3/3.3.0/g at.ui
# fixheader.sh - Corrige cabeçalho

sed -i 's/3.3/3.3.0/g' at.ui
Linha 173: Linha 166:
Linha 175: Linha 167:

chmod 700 myscript e resolvido !
}}}

Para executá-lo:

{{{
$ chmod 700 fixheader.sh
$ ./fixheader.sh
}}}
Linha 180: Linha 178:
Vamos colocar um valor default para a widget QDate``Time``Edit. Essa widget espera um valor QDate``Time como parâmetro no método set``Date``Time, então vamos criar um. Mas como a gente define a hora do QDate``Time? Examinando a documentação, vemos que o método setTime_t nos permite definir a data com a hora em segundos a partir do epoch Unix. Nós podemos pegar isso da função time() no módulo interno time.

Aqui está o código que faz isso. Nós colocaremos isso no métodos `__init__` para que ele seja populado corretamente já no início. Lembre de importar time!

  {{{
        
# Set the date to now
     now = QDateTime()
     now.setTime_t(time.time()) # Time in seconds since Unix Epoch
        self.time.setDateTime(now)
Vamos colocar um valor default para a widget QDate``Time``Edit. Essa widget espera um valor QDate``Time como parâmetro no método set``Date``Time, então vamos criar um. Mas como a gente define a hora do QDate``Time? Examinando a documentação, vemos que o método setTime_t nos permite definir a data com a hora em segundos a partir do epoch Unix. Nós podemos pegar isso da função {{{time()}}} no módulo interno {{{time}}}.

Aqui está o código que faz isso. Nós colocaremos isso no método {{{__init__}}} para que ele seja populado corretamente já no início. Lembre de importar {{{time}}} ({{{import time}}}!

{{{
#!python
    #
Set the date to now
    now = QDateTime()
    now.setTime_t(time.time()) # Time in seconds since Unix Epoch
    self.time.setDateTime(now)
Linha 201: Linha 200:
Deixe-me esclarecer a distinção entre um Signal/Slot C++ de um Signal/Slot Python. Não tem a ver com onde o objeto foi criado. Tem tudo a ver com onde o sinal foi originado, e onde o slot está localizado. Por exemplo, um QPush``Button tem um sinal C++, "clicked()". Se você criar sua própria subclasse no Python, chamado "Py``Push``Button", ele ainda terá um sinal C++, "clicked()". Se você criou um novo sinal em Python, chamado "Gobbledy``Gook()", então esse é um sinal Python, porque nada em C++ sequer sabe de sua existência. Deixe-me esclarecer a distinção entre um Signal/Slot C++ de um Signal/Slot Python. Não tem a ver com onde o objeto foi criado. Tem tudo a ver com onde o sinal foi originado, e onde o slot está localizado. Por exemplo, um QPush``Button tem um sinal C++, "{{{clicked()}}}". Se você criar sua própria subclasse no Python, chamado "Py``Push``Button", ele ainda terá um sinal C++, "{{{clicked()}}}". Se você criou um novo sinal em Python, chamado "{{{Gobbledy``Gook()}}}", então esse é um sinal Python, porque nada em C++ sequer sabe de sua existência.
Linha 209: Linha 208:
    {{{ {{{
#!python
Linha 213: Linha 213:
  * Ligar um sinal C++ a um sinal C++   * Ligar um sinal C++ a um slot C++
Linha 217: Linha 217:
    {{{ {{{
#!python
Linha 223: Linha 224:
    Você provavelmente não usará muitos sinais Python, mas aqui está como fazê-lo. Basicamente, você imagina um novo nome pra um sinal. Então você muda a palavra "SIGNAL" acima para "PYSIGNAL".

    Se você está usando muitos sinais Python, poderá fazer sentido ignorar a biblioteca Qt de sinais e usar a sua própria. Se você tem planos de portar para C++ um dia, isso não fará sentido algum. Eu fiz isso porque achei a sintaxe meio difícil, e debugar complicado.

   Nossa aplicação vai responder a apenas um sinal: o botão "Schedule" sendo pressionado. O que ele fará é rodar o comando "at" com argumentos apropriados.
    Você provavelmente não usará muitos sinais Python, mas aqui está como fazê-lo. Basicamente, você imagina um novo nome pra um sinal. Então você muda a palavra "{{{SIGNAL}}}" acima para "{{{PYSIGNAL}}}".

    Se você está usando muitos sinais Python, poderá fazer sentido ignorar a biblioteca QT de sinais e usar a sua própria. Se você tem planos de portar para C++ um dia, isso não fará sentido algum. Eu fiz isso porque achei a sintaxe meio difícil, e debugar complicado.

   Nossa aplicação vai responder a apenas um sinal: o botão "Schedule" sendo pressionado. O que ele fará é rodar o comando "{{{at}}}" com argumentos apropriados.
Linha 230: Linha 231:
  {{{ {{{
Linha 238: Linha 239:
Repare que estamos conectando um sinal C++ a um slot Python. Entretanto, esse slot não existe ainda. Vamos adicioná-lo à classe 'at'.

  {{{
Repare que estamos conectando um sinal C++ a um Slot Python. Entretanto, esse Slot não existe ainda. Vamos adicioná-lo à classe 'at'.

{{{
Linha 257: Linha 258:
Se houver algo no command box, nós abriremos um pipe para o comando 'at'. 'at' espera que o comando venha do stdin. Então nós escreveremos no seu stdin o comando que queremos executar. Note que nós não efetuamos nenhum tipo de checagem de erro aqui.

Prossiga e rode o aplicativo agora, e use o comando 'atq' para ver se o comando 'at' foi enfileirado.
Se houver algo no command box, nós abriremos um pipe para o comando '{{{at}}}'. '{{{at}}}' espera que o comando venha do {{{stdin}}}. Então nós escreveremos no seu {{{stdin}}} o comando que queremos executar. Note que nós não efetuamos nenhum tipo de checagem de erro aqui.

Prossiga e rode o aplicativo agora, e use o comando '{{{atq}}}' para ver se o comando '{{{at}}}' foi enfileirado.
Linha 263: Linha 264:
Aqui está o código final para o arquivo 'at.py'.

  {{{
Aqui está o código final para o arquivo '{{{at.py}}}'.

{{{
Linha 311: Linha 312:
Com o tempo restante, você poderá querer adicionar algumas extensões.

  * Usando o Qt Designer, adicione QLabels para descrever o quê cada um dos inputs fazem. Note que você não tem que mudar nada no código, só editar o arquivo 'at.ui' e rodar 'make'.

* Depois que você terminar o job 'at', você pode querer que a aplicação feche. Ache um slot apropriado para fechar o widget 'at' ou toda a aplicação. Pergunta: Porquê fechando o widget 'at' a aplicação toda fecha também?

* Cheque erros quando você chama o comando 'at'. Se houver alguma mensagem, mostre-a ao usuário com uma QMessage``Box.

* Escreva uma aplicação para listar a queue 'at'.

* Adicione funcionalidade para editar ou remover jobs 'at' enfileirados. Tente reutilizar o máximo de código possível. (Dica: a widget que irá requisitar um novo job 'at' já está pronta. Tente colocá-la em um QDialog.)

== Direções Futuras ==

Essa aplicação poderia ser parte de um conjunto de interfaces para comando Unix. Que outros comandos você gostaria de implementar? Eu sugeriria tentar comandos como "crontab" e "ps". Interpretar a saída desses comandos não é muito difícil, e as interfaces são bem fáceis.

Você também pode tentar combinar seus novos aplicativos com o seu programa 'at'. Se quiser, pode vendê-los como uma interface gráfica para Unix, mas você terá que comprar a licença comercial para ambos !PyQt e Qt a não ser que você siga algo como a GPL.
Com o tempo restante, você poderia adicionar algumas extensões.

  * Usando o Qt Designer, adicione QLabels para descrever o quê cada um dos inputs fazem. Note que você não tem que mudar nada no código, só editar o arquivo '{{{at.ui}}}' e rodar '{{{make}}}'.
  * Depois que você terminar o job '{{{at}}}', você pode querer que a aplicação feche. Ache um slot apropriado para fechar o widget '{{{at}}}' ou toda a aplicação. Pergunta: Porquê fechando o widget 'at' a aplicação toda fecha também?
  * Cheque erros quando você chama o comando '{{{at}}}'. Se houver alguma mensagem, mostre-a ao usuário com uma QMessage``Box.
  * Escreva uma aplicação para listar a queue '{{{at}}}'.
  * Adicione funcionalidade para editar ou remover jobs '{{{at}}}' enfileirados. Tente reutilizar o máximo de código possível. (Dica: a widget que irá requisitar um novo job '{{{at}}}' já está pronta. Tente colocá-la em um QDialog.)

== Dicas para o futuro ==

Essa aplicação poderia ser parte de um conjunto de interfaces para comando Unix. Que outros comandos você gostaria de implementar? Eu sugeriria tentar comandos como "{{{crontab}}}" e "{{{ps}}}". Interpretar a saída desses comandos não é muito difícil, e as interfaces são bem fáceis.

Você também pode tentar combinar seus novos aplicativos com o seu programa '{{{at}}}'. Se quiser, pode vendê-los como uma interface gráfica para Unix, mas você terá que comprar a licença comercial para ambos !PyQt e Qt a não ser que você siga algo como a GPL.
Linha 331: Linha 328:
O original em inglês, gentilmente cedido por Jonathan Gardner, está disponível em http://www.python.org/cgi-bin/moinmoin/JonathanGardnerPyQtTutorial . Traduzido por RodrigoVieira. O original em inglês, gentilmente cedido por Jonathan Gardner, está disponível [[http://www.python.org/cgi-bin/moinmoin/JonathanGardnerPyQtTutorial|aqui]].

----

Traduzido por RodrigoVieira.

Tutorial de PyQt

Este é um pequeno tutorial para iniciantes em PyQt. Assume-se que o leitor possua algum conhecimento prévio de Bash, Python e Qt.

Sumário

Este tutorial cobrirá os seguintes pontos:

  • Como utilizar o Qt Designer para gerar arquivos .ui para Qt.

  • Como usar a ferramenta pyuic para gerar programas Python.

  • Como usar sinais e slots do Qt no Python.
  • Finalmente, criaremos uma aplicação simples para comunicar com o comando 'at'.

Requisitos

Você precisará:

  • Red Hat 8.0 com a seguinte configuração:
    • qt-devel RPM instalado
    • PyQt-devel RPM instalado

Não precisa ser RPM, desde que você instale qt-devel e PyQt-devel. Essas bibliotecas já vêm na maioria das distribuições, como Mandrake, Suse, Fedora, etc. -- RodrigoVieira

PyQt funciona em outros sistemas também. Este tutorial pode ou não funcionar em outras configurações, porém eu não irei prover todos os detalhes sobre como fazê-los funcionar em outros sistemas. Você é responsável por resolver essa questão.

Para seguir esse tutorial, você deverá saber:

  • Como usar um editor de textos, e como fazer esse editor trabalhar apropriadamente com código Python.
  • Como programar em Python.
  • Alguns comandos Bash básicos.
  • Conhecimento básico de programação em Qt.

Se você não possui todos os conhecimentos acima, poderá ter alguns problemas para seguir este tutorial.

Utilizando o Qt Designer

Primeiramente, vamos começar onde eu começo. Abra uma janela de comando Bash e inicie o Qt Designer digitando o seguinte comando:

$ designer

Eu vou assumir que você não seja totalmente iniciante na utilização do Qt Designer. Caso seja, você pode facilmente consultar a documentação.

Crie uma widget nova. Nomeie-a 'at_auto'. Adicione algumas coisas nela:

  • Adicione uma QLineEdit. Nomeie-o "command" na janela de propriedades.

  • Adicione um QPushButton. Nomeie-o "schedule" na janela de propriedades. Mude o seu texto para "Schedule".

  • Adicione um QDateTimeEdit. Nomeie-o "time" na janela de propriedades.

Agora, acerte o layout usando as ferramentas de layout do Qt, do jeito que você preferir. Você deve querer utilizar alguns espaçadores também.

Salve o arquivo em um diretório para esse tutorial. Se você ainda não o criou, crie um chamado "pyqt_tutorial" ou algo assim. Salve o arquivo como "at.ui".

Utilizando o pyuic

Volte ao prompt do Bash, ou abra uma nova janela de comando. Vá ao diretório que você acabou de criar, e execute o seguinte comando.

$ pyuic at.ui

Esse comando irá imprimir em stdout o arquivo Python gerado a partir do arquivo .ui do Qt. Nós queremos salvar essa saída, então nós rodaremos o seguinte comando.

$ pyuic at.ui > at_auto.py

Usar o pyuic dessa forma pode ser errado. pyuic escreve algumas informações na saída-padrão que não são código Python "de verdade"! Use-o dessa forma:

$ pyuic at.ui -o at_auto.py

-- Petr Vanek (subzero AT py DOT cz)

Este problema não afeta o pyuic que vem com PyQt 3.5 (que é o que RedHat 8 e 9 usam). Eu postei um patch na lista PyKDE para consertar isso. Phil a aceitou. Deve aparecer na próxima versão (3.7?) -- Jonathan Gardner

Nós depositamos a saída do comando em at_auto.py. Toda vez que alterarmos o arquivo at.ui, precisaremos regerar o arquivo at_auto.py. Vamos adicionar esse comando a um Makefile.

# Arquivo: Makefile
at_auto.py: at.ui
        pyuic at.ui > at_auto.py

/!\ Note que nós usamos Tab no arquivo Makefile.

Obs: Se você está usando PyQt 3.6, você terá que usar o método '-o' descrito por Petr acima. Você pode querer usar de qualquer forma, por outro motivo:

# Arquivo: Makefile
at_auto.py : at.ui
        pyuic at.ui -o at_auto.py

Agora execute o Makefile.

$ make

Note que ele diz algo sobre todos os arquivos estarem atualizados. Vamos executar um touch em at.ui para que ele conste como mais recente que at_auto.py, e então executar make novamente.

$ touch at.ui
$ make

Agora ele ecoou os comandos executados. Veja que ele regenerou at_auto.py com sucesso.

Teoria

A idéia aqui é que você quer que o desenvolvedor GUI seja capaz de fazer alterações na interface (como mudar a posição de objetos) sem afetar a lógica por trás da GUI. Então com a configuração que temos agora, tudo o que o desenvolvedor GUI tem que fazer é usar Qt Designer para mudar o arquivo .ui, e então rodar o comando make para ver suas alterações serem efetuadas.

Seu arquivo make ficará mais complicado à medida que você adiciona mais arquivos. Mas não esqueça de ler mais sobre make para que você possa saber de antemão como utilizá-lo apropriadamente.

Rodando sua aplicação

Então nós temos aquele arquivo .ui, e também o arquivo at_auto.py. E agora?

Nós temos que criar at.py, o programa principal. Aqui está o código:

from qt import *
from at_auto import at_auto

class at(at_auto):
    def __init__(self, parent=None, name=None, fl=0):
        at_auto.__init__(self,parent,name,fl)

if __name__ == "__main__":
    import sys
    a = QApplication(sys.argv)
    QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()"))
    w = at()
    a.setMainWidget(w)
    w.show()
    a.exec_loop()

Agora é só executá-lo.

$ python at.py

Pronto! Você tem o seu programa.

NOTA: Se você está usando Qt Designer com a versão 3.3.0 do Qt, seu arquivo .ui contém:

<!DOCTYPE UI>
<UI version="3.3" stdsetdef="1">

no cabeçalho, e pyuic (3.8.1 pelo menos) reclamará que essa versão é muito recente e não produzirá nenhuma saída.

Para resolver isso da forma fácil, você pode criar um pequeno script que irá corrigir esse erro automaticamente, execute make e rode seu novo programa:

# fixheader.sh - Corrige cabeçalho

sed -i 's/3.3/3.3.0/g' at.ui
make
exec python at.py

Para executá-lo:

$ chmod 700 fixheader.sh
$ ./fixheader.sh

Colocando Data / Hora Default

Vamos colocar um valor default para a widget QDateTimeEdit. Essa widget espera um valor QDateTime como parâmetro no método setDateTime, então vamos criar um. Mas como a gente define a hora do QDateTime? Examinando a documentação, vemos que o método setTime_t nos permite definir a data com a hora em segundos a partir do epoch Unix. Nós podemos pegar isso da função time() no módulo interno time.

Aqui está o código que faz isso. Nós colocaremos isso no método __init__ para que ele seja populado corretamente já no início. Lembre de importar time (import time!

   1     # Set the date to now
   2     now = QDateTime()
   3     now.setTime_t(time.time()) # Time in seconds since Unix Epoch
   4     self.time.setDateTime(now)

Esse trecho de código mostra como Python e PyQt trabalham facimente um com o outro. Também demonstra a linha de pensamento que você deverá seguir para manipular widgets do Qt.

Sinais e Slots

Tudo que você tem que fazer agora é conectar sinais (Signals) a Slots. É bem fácil, e por isso que eu gosto do PyQt.

Python não é C++. Então ele lida com sinais e slots de forma diferente.

Primeiro, em Python, tudo que pode ser chamado é um slot. Pode ser um método, uma função, ou mesmo uma expressão lambda. Segundo, em Python, um sinal é só um texto sem significado em particular.

Deixe-me esclarecer a distinção entre um Signal/Slot C++ de um Signal/Slot Python. Não tem a ver com onde o objeto foi criado. Tem tudo a ver com onde o sinal foi originado, e onde o slot está localizado. Por exemplo, um QPushButton tem um sinal C++, "clicked()". Se você criar sua própria subclasse no Python, chamado "PyPushButton", ele ainda terá um sinal C++, "clicked()". Se você criou um novo sinal em Python, chamado "Gobbledy``Gook()", então esse é um sinal Python, porque nada em C++ sequer sabe de sua existência.

Quando você liga um sinal a um slot, você pode fazer uma das seguintes operações:

  • Ligar um sinal C++ a um slot Python
    • Você vai fazer isso o tempo todo. Isso é feito através de uma chamada assim:

   1 QObject.connect(some_object, SIGNAL('toggled(bool)'), some_python_callable)
  • Ligar um sinal C++ a um slot C++
    • Você não fará isso muito freqüentemente, mas pode ser útil às vezes.

   1 QObject.connect(some_object, SIGNAL('toggled(bool)'), some_object, SLOT('the_slot(bool)'))
  • Ligar um sinal Python a um slot C++ ou Python
    • Você provavelmente não usará muitos sinais Python, mas aqui está como fazê-lo. Basicamente, você imagina um novo nome pra um sinal. Então você muda a palavra "SIGNAL" acima para "PYSIGNAL". Se você está usando muitos sinais Python, poderá fazer sentido ignorar a biblioteca QT de sinais e usar a sua própria. Se você tem planos de portar para C++ um dia, isso não fará sentido algum. Eu fiz isso porque achei a sintaxe meio difícil, e debugar complicado.

    • Nossa aplicação vai responder a apenas um sinal: o botão "Schedule" sendo pressionado. O que ele fará é rodar o comando "at" com argumentos apropriados.

Aqui está o código para iniciar a conexão:

self.connect(
    self.schedule, SIGNAL('clicked()'),
    self.schedule_clicked
)

Repare que estamos conectando um sinal C++ a um Slot Python. Entretanto, esse Slot não existe ainda. Vamos adicioná-lo à classe 'at'.

    def schedule_clicked(self):
        if not str(self.command.text()):
            QMessageBox.critical(self,
                "Invalid event", "You must specify an event",
                QMessageBox.Ok)
            return

        t = str(self.time.dateTime().toString('hh:mm MM/dd/yyyy'))
        p = os.popen('at -m "%s"'%t, 'w')
        p.write(str(self.command.text()))
        self.close()

O processo aqui se divide em duas partes. Primeiro, nós checamos se algo é especificado no widget QLineEdit "command". Caso não haja nada, nós mostramos uma QMessageBox com um erro crítico.

Se houver algo no command box, nós abriremos um pipe para o comando 'at'. 'at' espera que o comando venha do stdin. Então nós escreveremos no seu stdin o comando que queremos executar. Note que nós não efetuamos nenhum tipo de checagem de erro aqui.

Prossiga e rode o aplicativo agora, e use o comando 'atq' para ver se o comando 'at' foi enfileirado.

Tudo certo? OK.

Aqui está o código final para o arquivo 'at.py'.

from qt import *
from at_auto import at_auto
import time
import sys
import os

class at(at_auto):

    def __init__(self, parent=None, name=None, fl=0):
        at_auto.__init__(self,parent,name,fl)

        # Set the date to now
        now = QDateTime()
        now.setTime_t(time.time()) # Time in seconds since Unix Epoch
        self.time.setDateTime(now)

        self.connect(
            self.schedule, SIGNAL('clicked()'),
            self.schedule_clicked
        )

    def schedule_clicked(self):
        if not str(self.command.text()):
            QMessageBox.critical(self,
                "Invalid event", "You must specify an event",
                QMessageBox.Ok)
            return

        t = str(self.time.dateTime().toString('hh:mm MM/dd/yyyy'))
        p = os.popen('at -m "%s"'%t, 'w')
        p.write(str(self.command.text()))
        self.close()

if __name__ == "__main__":
    a = QApplication(sys.argv)
    QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()"))
    w = at()
    a.setMainWidget(w)
    w.show()
    a.exec_loop()

Dever de Casa

Com o tempo restante, você poderia adicionar algumas extensões.

  • Usando o Qt Designer, adicione QLabels para descrever o quê cada um dos inputs fazem. Note que você não tem que mudar nada no código, só editar o arquivo 'at.ui' e rodar 'make'.

  • Depois que você terminar o job 'at', você pode querer que a aplicação feche. Ache um slot apropriado para fechar o widget 'at' ou toda a aplicação. Pergunta: Porquê fechando o widget 'at' a aplicação toda fecha também?

  • Cheque erros quando você chama o comando 'at'. Se houver alguma mensagem, mostre-a ao usuário com uma QMessageBox.

  • Escreva uma aplicação para listar a queue 'at'.

  • Adicione funcionalidade para editar ou remover jobs 'at' enfileirados. Tente reutilizar o máximo de código possível. (Dica: a widget que irá requisitar um novo job 'at' já está pronta. Tente colocá-la em um QDialog.)

Dicas para o futuro

Essa aplicação poderia ser parte de um conjunto de interfaces para comando Unix. Que outros comandos você gostaria de implementar? Eu sugeriria tentar comandos como "crontab" e "ps". Interpretar a saída desses comandos não é muito difícil, e as interfaces são bem fáceis.

Você também pode tentar combinar seus novos aplicativos com o seu programa 'at'. Se quiser, pode vendê-los como uma interface gráfica para Unix, mas você terá que comprar a licença comercial para ambos PyQt e Qt a não ser que você siga algo como a GPL.

Sobre esse tutorial

O original em inglês, gentilmente cedido por Jonathan Gardner, está disponível aqui.


Traduzido por RodrigoVieira.