[newbie] advice and comment wanted on first tkinter program

Peter Otten __peter__ at web.de
Fri Jan 17 10:17:57 EST 2014


Jean Dupont wrote:

> Dear all,
> I made a simple gui with tkinter. I can imagine there are things which I
> did which are "not optimal". So what I ask is to comment on my code
> preferable with snippets of code which show how to do improve my code.
> #!/usr/bin/env python
> import Tkinter
> import time
> import RPi.GPIO as GPIO
> GPIO.setmode(GPIO.BOARD)
> GPIO.setup(26,GPIO.OUT)
> GPIO.setup(24,GPIO.OUT)
> #hardware : connect 2 leds:
> #board-pin 26 on/off led; control with buttons
> #board-pin 24 led with pwm dimming and frequency; control via sliders
> top = Tkinter.Tk()
> top.geometry("600x400+310+290")
> var1 = DoubleVar()
> var2 = DoubleVar()
> i=0
> p=GPIO.PWM(24,1)
> p.start(50)
> def btn_on_cmd():
>         text3.configure(bg = "#00FF00")
>         text3.delete(0.1,END)
>         text3.insert("0.1","ON ")
>         GPIO.output(26,True)
> def btn_off_cmd():
>         text3.configure(bg = "#FF4000")
>         text3.delete(0.1,END)
>         text3.insert("0.1","OFF")
>         GPIO.output(26, False)
> def timer0():
>         global i
>         i=i+1
>         text1.delete(0.1,END)
>         text1.insert(4.2,"Timer: " + str(i))
>         label1.configure(text=time.strftime("%H:%M:%S"))
>         top.after(1000, timer0)
> def Set_PWM(var1):
>         DC = float(var1)
>         p.ChangeDutyCycle(DC)
> def Set_FREQ(var2):
>         FR = float(var2)
>         p.ChangeFrequency(FR)
> btn_on = Button(top, text ="On", command = btn_on_cmd)
> btn_on.place(x=10,y=100)
> btn_off = Button(top, text ="Off", command = btn_off_cmd)
> btn_off.place(x=100,y=100)
> text1 =Text(top, bg = "#009BFF", font=("Helvetica",14), height = 1, width
> = 15)
> text1.place(x=5,y=5)
> text3=Text(top, bg = "red", font=("Helvetica",12),height = 1, width = 4)
> text3.place(x=60,y=60)
> label1 = Label(top,relief=RAISED,bg =
> "#EFF980",font=("Helvetica",14),height = 1, width = 15)
> label1.place(x=5,y=350)
> label2= Label(top,relief=RAISED,bg =
> "#BFBFBF",font=("Helvetica",10),height = 1, text= "Freq (Hz)")
> label2.place(x=420,y=320)
> label3= Label(top,relief=RAISED,bg =
> "#BFBFBF",font=("Helvetica",10),height = 1, text= "DC %")
> label3.place(x=520,y=320)
> slider1 = Scale(top,variable = var1,length = 300,resolution = 1,command  =
> Set_PWM)
> slider1 = Scale(top,variable = var1,length = 300,resolution = 1,command  =
> Set_PWM) slider1.place(x=500,y=5)
> slider1.set(50)
> slider2 = Scale(top,variable = var2,length = 300,from_= 0.1, to =
> 50,resolution = 0.1,command  = Set_FREQ) slider2.place(x=400,y=5)
> slider2.set(2)
> timer0()
> top.mainloop()
> GPIO.cleanup()
> 
> 
> thanks in advance
> jean

First and foremost a program has to do what the author wants it to do. 
Everything else is secondary. You are likely to have such a program on your 
machine, so congrats :)

However, the version you posted does not run, probably because you started 
to replace

from Tkinter import *
top = Tk()
...
var1 = DoubleVar()

with the -- better --

import Tkinter
top = Tkinter.Tk()
...

but stopped too early, so that the line

var1 = DoubleVar

will raise a NameError. The fix is mechanical: run the program, go to the 
line with the NameError and add the 'Tkinter.' prefix.

Once you have done that you should take the time to find good variable 
names. var1? I have no idea what value that could hold until I've read the 
whole program. That's feasible here, but program size may grow over time, 
and can you still tell me what var1 means next week? I recommend names that 
reflect the problem domain, e. g. `var_dutycycle` or just `dutycycle`.

Next you should consider grouping the code by topic -- not necessarily into 
functions; having a section that does the setup for the dutycycle data and 
widgets and one for the time etc., visually separated by one or two empty 
lines should be sufficient.

If you're ambitious you should read up on the grid layout manager. I allows 
widget size to change depending on the window size.

The Text widget can be used to write whole Editors (like IDLE) -- it does no 
harm here, but seems a bit heavyweight for just an On/Off display. I would 
go with a Label or Entry. 

What does a red widget with no text mean, by the way? On, off, or undefined?
Personally, I like to start with a defined state. An easy way to achieve 
this is to call 

button_off_cmd() # or button_on_cmd()

manually before your program enters the mainloop() -- just like you did with 
timer0().

PS: An easy way to get an idea of what a script does is to run it. I'd guess 
that by keeping the Rasperry-Pi-specific code in you are reducing the number 
of readers who can do that by a few orders of magnitude. I managed to get it 
to run with the following ad-hoc changes:

$ diff -u raspberry_orig.py raspberry_mock.py
--- raspberry_orig.py   2014-01-17 16:10:20.843334832 +0100
+++ raspberry_mock.py   2014-01-17 16:10:58.970855503 +0100
@@ -1,7 +1,36 @@
 #!/usr/bin/env python
 import Tkinter
+from Tkinter import *
 import time
-import RPi.GPIO as GPIO
+
+try:
+    import RPi.GPIO as GPIO
+except ImportError:
+    class Name(str):
+        def __repr__(self):
+            return self
+
+    class GPIO:
+        def __init__(self, prefix):
+            self.prefix = prefix
+        def __getattr__(self, name):
+            if name in ["BOARD", "OUT"]:
+                return Name(name)
+            if name == "PWM":
+                return GPIO("PWM")
+            if name == "__call__":
+                def c(*args):
+                    print "Initializing {}{}".format(self.prefix, args)
+                    return self
+                return c
+            def f(*args):
+                print "Calling {}.{}{}".format(self.prefix, name, args)
+            return f
+
+    GPIO = GPIO("GPIO")
+
+
+
 GPIO.setmode(GPIO.BOARD)
 GPIO.setup(26,GPIO.OUT)
 GPIO.setup(24,GPIO.OUT)





More information about the Python-list mailing list