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

Revisão 1e 2008-06-15 03:52:51

Excluir mensagem

CalculadoraTkGtkQt

by JoaoBueno

Nunca programei muita coisa em GUI. Hoje inventei de criar uma calculadora como exemplo para um amigo que está aprendendo python.

É impressionante como os kits GUI - todos - são anti-pythonicos. Nas chamadas, acesso a atributos, etc... No quesito ser Pythonico, o Tkinter de fato ganha de longe.

Agora GTK e Qt...nota -5 em documentação. Para conseguir fazer o exemplo, não achei sequer um tutorial dos bindings de python que tivesse coisas tão simples quando o que fazer para acessar o texto de um widget de entrada de texto de uma linha (no Qt até pra saber que esse Widget se chamava QLineEdit sambei). O remédio para Qt e GTK é consultar a documentação na raiz mesmo - do [http://www.gtk.org/documentation.html GTK+ em C] e do [http://doc.trolltech.com/4.0/index.html QT4 em C++]

Mas, esperoq eu minah calculadorinha 4 operações possa salvar uma ou outra alma por ai, com dicas de como conectar botões em ter que criar uma função para cada botão. (Nesse critério, o mais esotérico é o Tk :-) )- o Qt tá mal documentado pra caramba, mas pelo menos tem só "um jeito óbvi de fazer". Alias - teoricamente, a gambiarra que fiz no Tkinter deveria ter funcionado no Qt também - mas não funciona. Coisas de nossos amigos em C++.

Por fim, desculpas aos puristas de algum desses toolkits - provavelmente passei longe de "fazer do jeito certo" em qualquer das três implementações. O Tkinter sofreu muito por não estar redimensionável (tem como fazer).

Mas por outro lado, o fato do Qt estar mais bonito é culpa, sim, do GTK e do Tk - nesses, para espaçar os botões, por exemplo, eu teria que ficar fazendo ajustes manuais do layout que não sãp do escopo dessa aplicação. Em compensação, o Qt perde feio no número de linhas necessárias para implementação da interface. 50% a mais!!

   1 #!/usr/bin/env python
   2 # -*- coding: utf-8 -*-
   3 import sys
   4 
   5 class Calculator(object):
   6     def __init__(self):
   7         self.reset()
   8 
   9     def reset(self):
  10         self.set_displayed(0.)
  11         self.operator = None
  12         self.operand_1 = 0.
  13         self.clear_display = True
  14 
  15     def keypress(self, arg):
  16         if arg in ("+", "-", "X", "/"):
  17             if not self.operator:
  18                 self.operand_1 = self.get_displayed()
  19             else:
  20                 self.operand_2 = self.get_displayed()
  21                 self.operate()
  22             self.operator = arg
  23             self.clear_display = True
  24         elif arg == "%":
  25             if not self.operand_1:
  26                 return
  27             operand_2 = self.get_displayed()
  28             result = self.operand_1 / 100.0 * operand_2
  29             self.set_displayed(result)
  30         elif arg == "=":
  31             if not self.operator:
  32                 return
  33             self.operand_2 = self.get_displayed()
  34             self.operate()
  35             self.operator = None
  36             self.clear_display = True
  37         elif arg == "C":
  38             self.reset()
  39         else:
  40             self.insert_number(arg)
  41 
  42 
  43     def set_displayed(self, number):
  44         self.set_displayed_str(("%f" % number).strip("0"))
  45     
  46     def get_displayed(self):
  47         try:
  48             result = float(self.get_displayed_str())
  49         except ValueError:
  50             result = 0.0
  51         return result
  52 
  53     def insert_number(self, digit):
  54         if not self.clear_display:
  55             text = self.get_displayed_str()
  56         else:
  57             text = ""
  58         text += digit
  59         self.set_displayed_str(text)
  60         self.clear_display = False
  61 
  62     def operate (self):
  63         if self.operator == "+":
  64             result = self.operand_1 + self.operand_2
  65         elif self.operator == "-":
  66             result = self.operand_1 - self.operand_2
  67         elif self.operator == "X":
  68             result = self.operand_1 * self.operand_2
  69         elif self.operator == "/":
  70             if not self.operand_1:
  71                 self.set_displayed_str("Error")
  72                 self.operand_1 = 0
  73                 return
  74             result = self.operand_1 / self.operand_2
  75         self.operand_1 = result
  76         self.set_displayed(result)
  77 
  78 try:
  79     import gtk
  80     import pango
  81 except ImportError:
  82     sys.stderr.write("PyGTK not found\n")
  83     #create a dummy gtk window class so the program parses
  84     class gtk(object):
  85         class Window(object):
  86             pass
  87 
  88 class GtkCalculator(Calculator, gtk.Window):
  89     def __init__(self):
  90         gtk.Window.__init__(self)
  91         self.connect("destroy", self.destroy)
  92         mainbox = gtk.VBox()
  93         self.add(mainbox)
  94         self.display = gtk.Entry()
  95         mainbox.add(self.display)
  96         self.display.set_editable(False)
  97         self.display.set_alignment(1.0)
  98         count = 0
  99         font = pango.FontDescription("monospace 16")
 100         buttons = "789+456-123X0.%/C="
 101         for button in buttons:
 102             if count % 4 == 0:
 103                 row = gtk.HBox()
 104                 mainbox.pack_start(row)
 105             b = gtk.Button(button)
 106             #b is a GTK Button - it has no text
 107             # it contains a single "gtk label"
 108             # to set the button font we have to change the font
 109             # in this contained label instead
 110             b.get_child().modify_font(font)
 111             b.connect("clicked", self.keypress, button)
 112             row.pack_start(b)
 113 
 114             count += 1
 115         self.show_all()
 116         Calculator.__init__(self)
 117 
 118     def keypress(self, widget, arg):
 119         Calculator.keypress(self, arg)
 120 
 121     def set_displayed_str(self, number):
 122         self.display.set_text(number)
 123     def get_displayed_str(self):
 124         return self.display.get_text()
 125 
 126 
 127     def destroy(self, *args):
 128         gtk.main_quit()
 129 
 130 try:
 131     import Tkinter
 132 except ImportError:
 133     sys.stderr.write("Tkinter not found\n")
 134     #create a dummy Tkinter.Tk class so the program parses
 135     class Tkinter(object):
 136         class Tk(object):
 137             pass
 138 
 139 class TkCalculator(Calculator, Tkinter.Tk):
 140     def __init__(self):
 141         Tkinter.Tk.__init__(self)
 142         self.title("Calculadora")
 143         self.displayed = Tkinter.StringVar()
 144         self.display = Tkinter.Entry(self,
 145                                      textvar = self.displayed,
 146                                      state = "readonly",
 147                                      justify = Tkinter.RIGHT,
 148                                      background="#dddddd",
 149                                      readonlybackground="#dddddd",
 150                                      borderwidth=5)
 151         self.display.grid(row=0, columnspan=4)
 152         buttons = "789+456-123X0.%/C="
 153         count = 0
 154         row = 0
 155         for button in buttons:
 156             if count % 4 == 0:
 157                 row += 1
 158             b = Tkinter.Button(self,
 159                                text = button,
 160                                command = self.command_factory(button),
 161                                width=2 )
 162             b.grid(column=count % 4, row=row)
 163             count += 1
 164         Calculator.__init__(self)
 165 
 166     def command_factory(self, button):
 167         class Command(object):
 168             def __init__(self, calculator, button):
 169                 self.calculator = calculator
 170                 self.button = button
 171             def command(self):
 172                 return self.calculator.keypress(self.button)
 173         return Command(self, button).command
 174 
 175 
 176     def set_displayed_str(self, string):
 177         self.displayed.set(string)
 178 
 179     def get_displayed_str(self):
 180         return self.displayed.get()
 181 
 182 try:
 183     from PyQt4 import QtGui
 184     from PyQt4 import QtCore
 185 except ImportError:
 186     sys.stderr.write("PyQt4 not found\n")
 187     #create a dummy Tkinter.Tk class so the program parses
 188     class QtGui(object):
 189         class QWidget(object):
 190             pass
 191 class QtCalculator(QtGui.QWidget, Calculator):
 192     def __init__(self):
 193         QtGui.QWidget.__init__(self)
 194         self.setWindowTitle("Calculator")
 195         grid = QtGui.QGridLayout()
 196         #FIXME: find single line widget
 197         self.display = QtGui.QLineEdit()
 198         self.display.setReadOnly(True)
 199         self.display.setAlignment(QtCore.Qt.AlignRight)
 200         grid.addWidget(self.display,0,0,1, 5)
 201         buttons = "789+456-123X0.%/C="
 202         counter = 0
 203         row = 0
 204         self.clear_display = False
 205         for button in buttons:
 206             if counter % 4 == 0:
 207                 row += 1
 208             b = QtGui.QPushButton()
 209             b.setText(button)
 210             self.connect(b, QtCore.SIGNAL("clicked()"), self.keypress)
 211             grid.addWidget(b, row, counter % 4)
 212             counter += 1
 213         self.setLayout(grid)
 214         self.show()
 215         Calculator.__init__(self)
 216 
 217     def command_factory(self, button):
 218         class Command(object):
 219             def __init__(self, calculator, button):
 220                 self.calculator = calculator
 221                 self.button = button
 222             def command(self):
 223                 return self.calculator.keypress(self.button)
 224         return Command(self, button).command
 225 
 226     def keypress(self, *args):
 227         button = QtCore.QObject.sender(self)
 228         if not isinstance(button, QtGui.QPushButton):
 229             return
 230         key = str(button.text().toUtf8())
 231         Calculator.keypress(self,key)
 232         
 233     def set_displayed_str(self, string):
 234         self.display.setText(string)
 235 
 236     def get_displayed_str(self):
 237         return str(self.display.text().toUtf8())
 238     
 239 if __name__ == "__main__":
 240     import sys
 241     if len(sys.argv) >= 2 and sys.argv[1] in ("tk", "gtk", "qt"):
 242         if sys.argv[1] == "gtk":
 243             GtkCalculator()
 244             gtk.main()
 245         elif sys.argv[1] == "tk":
 246             TkCalculator()
 247             Tkinter.mainloop()
 248         elif sys.argv[1] == "qt":
 249             app = QtGui.QApplication(sys.argv[1:])
 250             c = QtCalculator()
 251             sys.exit(app.exec_())
 252             
 253     else:
 254         sys.stderr.write("Usage: calc_tk_gtk [tk|gtk|qt]\n")
 255         sys.exit(1)