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)