appleberry

From Apple to Raspberry Pi: More Widgets! More Power!!!

appleberryWe covered some of the major simple widgets in Tkinter. There are a few more used less regularly: radio buttons, checkboxes, sliders, and spinners. This lesson, we’ll look at those  widgetsand make a better power panel.

Control Variables

We used control variables in the Button, Entry and Label widgets. The control variable in a button and label controlled the text and title programmatically using the textvariable attribute. In those cases, the control variable is optional. For the controls we are going to discuss, control variables are mandatory to work properly. Unlike Python variables, control variables are typed — they are classes of the Tkinter module. There are three types:

  • IntVar – integers and boolean values represented by 0 and 1
  • DoubleVar – floating point numbers
  • StringVar – strings

Declare a control variable by assigning it to the proper constructor:

my_bool = IntVar()
my_int = IntVar()
my_float = DoubleVar()
my_String =StringVar()

In the widget there is an attribute assigned to the control variable, such as value, textvariable or variable. The system will use the value to change the control or give you a value from the control.

When assigning values, control variables are not  Python variables. They are Tkinter objects. As objects, they have true getters and setters. Trying to use simple assignment will lead to bugs or exceptions:


#DON’T do this
a_String = my_String
my_String = ‘Hello World’

#DO this instead:
aString= my_String.get()
myString.set(‘Hello World’)

Making a Starting Control Panel

We have made buttons and labels in an earlier post.  We configured them in another. We made buttons with images in the last post.  To start this lesson and review what we covered before, here is a control panel with image buttons:

#Steve_sampler_02
#a demo program to show many Tkinter widgets
#05-01-14

from tkinter import *

#make the window
root=Tk()
root.title("Steve's Control Widget Sampler")

#make the frame
frame = Frame(root)
frame.grid(row=0, column=0)

#—————— Controller ————-

#control variables
label1_text = StringVar()

def infoPressed():
    label1_text.set('About')

def okPressed():
    label1_text.set('Start')
#—————— View ——————

#label
label1_text.set('Off')
label_1 = Label(frame, textvariable = label1_text)
label_1.grid(column = 0,row = 0)

# info button
button_1 = Button(frame, text = 'info',command= infoPressed)
button_1.grid(column = 0 , row = 5)
button_1_image= PhotoImage(file = 'glass_button_info.gif')
button_1.configure(image = button_1_image, relief = FLAT)

#ok button
button_2 = Button(frame, text = 'Ok', command = okPressed)
button_2.grid(column = 1 , row = 5)
button_2_image= PhotoImage(file = 'glass_button_OK.gif')
button_2.configure(image = button_2_image, relief = FLAT)

#exit button
button_3 = Button(frame, text = 'Exit', command = quit)
button_3.grid(column = 2 , row = 5)
button_3_image= PhotoImage(file = 'glass_button_exit.gif')
button_3.configure(image = button_3_image, relief = FLAT)

#main loop
mainloop()

We set up three buttons and a label. The label uses a control variable, and changes the message depending on the button we click. Each button uses one of the images below:
glass_button_exit

glass_button_info

glass_button_OK

You can make your own or download these buttons. Place the files in the same directory as your program. We set relief to FLAT to make the buttons look better. Build and run and you will see this:
Screenshot 2014-05-02 15.56.57

Make a Multi-line label with Message

The Message widget is a label which allows for multiple lines. Like C and Java, a new line escape sequence \n in a string will make for a new line. Message widgets set up like a label in almost every other aspect. Place this code above the label code:

#message
message1_text.set('Message Area \n Here‘)
message1 = Message(frame)
message1.grid(column = 1, row = 0, rowspan = 2,columnspan = 3, sticky = EW)
message1.configure(textvariable = message1_text, aspect = 150, width = 240)

We have another control variable for this widget. Add this to our control variable declaration

message1_text = StringVar()

Build and run. We now have a second message area that shows two lines.
Screenshot 2014-05-02 15.59.36

Make a Check Button

In earlier lessons we used a button as a toggle. Most people use a Checkbutton to do this. Check buttons usually have some visual indicator of the status on or off. Add this code above the message widget code:

# checkbutton
checkButton_1 = Checkbutton(frame,text = 'On',variable = isOn, command = onOffChanged)
checkButton_1.grid(column = 0, row = 1)

The Checkbutton has a mandatory control variable. Declare the control variable with the other variables.

isOn = IntVar()

Checkbutton uses an integer control variable as a boolean value. By default, false is 0 and true is 1. We did not initialize this control variable. Checkbutton sets this variable to 0 as a default. This code has a command attribute. Add a function below the other controller functions:

def onOffChanged():
    if isOn.get()== 1:
        mytext = 'System On'
    else:
        mytext = 'System Off'
    label1_text.set(mytext)

Build and run this code:
Screenshot 2014-05-02 15.59.50

We use the value of the check button to change the label. Click it back and forth a few times.

Make a Spin Box

The spin box is an incremental counter with up and down buttons. Add the following code above the Checkbutton code.

#spinbox
spin1 = Spinbox(frame, textvariable = spin1_text )
spin1.grid(column = 3, row = 5)
spin1.configure(from_ =0, to = 10, increment = 0.5, width = 5)
spin1.configure(font = ('Sans', '18', 'bold'))

Spin boxes use a range of values stored as a string. Add the following to the control variables.

spin1_text=StringVar()

Specify the range of values with the from_ and the to attributes. The increment is the amount added or subtracted from the count in the spin box. The width is the number of digits to show in the spin box. Build, run and try out  the current version of the control panel.
Screenshot 2014-05-02 16.01.03

Make Radio Buttons

Radio button share the Check boxes on/off status. Unlike the Checkbox, radio buttons work together, so that only one is on at one time. This works well for small lists of choices. Radio buttons use an attribute variable assigned to a control variable. Control variables are instantiated IntVar() or a StringVar(). Add this to our control variables:

radio_value = StringVar()

With the value attribute, set a value for each radio button. By default, that value sets the title text for the button. There are text, textvariable and image attributes if you want something other than the value as the title text. As radio buttons work as a group, you can make all the radio buttons using one instance variable for the widgets. Before adding the widgets, initialize the default value for the radio button. Add this code above the check button code:

#radiobuttons
radio_value.set(‘white')

radioButton = Radiobutton(frame,value = 'white',variable = radio_value)
radioButton.grid(column = 0, row = 4)
radioButton.configure(text = 'white')

#radiobuttons
radioButton = Radiobutton(frame,value = 'red',variable = radio_value)
radioButton.grid(column = 1, row = 4)
radioButton.configure(text = 'red')

#radiobuttons
radioButton = Radiobutton(frame,value = 'blue',variable = radio_value)
radioButton.grid(column = 2, row = 4)
radioButton.configure(text = 'blue')

#radiobuttons
radioButton = Radiobutton(frame,value = 'gray',variable = radio_value)
radioButton.grid(column = 3, row = 4)
radioButton.configure(text = 'gray')

Build and run. Each radio button, when pressed, indicates the user clicked it.
Screenshot 2014-05-02 16.02.30

Make a Scale Slider

Our last widget is a scale slider, which combines attribute of many of the widgets. Scale sliders are either horizontal or vertical sliders limited to a range of values with some interval between their values. Scale sliders, like spinners have from_ and to attributes to define the range. Instead of a increment, the attribute is a tickinterval to indicate the resolution of the slider. The control value for a scale slider is a number, and can be either a IntVar() or a DoubleVar(). If declared as a StringVar() the value returned will be the string representation of the number. Add this control variable to the control variable declarations:

scale1_text = StringVar()

One new attribute is orient. A value of HORIZONTAL will make a horizontal slider. A value of VERTICAL will make a vertical scale slider. VERTICAL is the default. Add this code above the code for the radio buttons:

#scale
scale1_text.set('0')
scale1 = Scale(frame,variable = scale1_text, command = sliderChanged)
scale1.grid(column =0, row =7, columnspan = 4, sticky = EW)
scale1.configure(orient = HORIZONTAL, from_ = 0, to = 25, tickinterval = 5)

The code includes command=sliderChanged. Add this below our other controller functions:

def sliderChanged(myScale ):    #needs a parameter
    if(isOn.get() == 1):
       #changeColors()
        voltage = myScale
        time = spin1_text.get()     #value of our spinner
        mystring = 'Power Panel!! \n Voltage at: '+ voltage+ 'V\n Time at: ' + time
        message1_text.set(mystring)
        label1_text.set('Change '+ voltage + 'V')
    else:
        label1_text.set("System Off")
        message1_text.set('Standby Mode')

Scale sliders always pass a parameter of its value to the specified command. The function called must have one parameter or will cause an exception. This parameter is equal to the control variable. If the function uses the value and nothing else does, the control variable is not necessary. You can also use a getter to the scale slider instance if you don’t want a variable at all. For example

voltage = scale1.get()

works as well as a control variable.

In this function, there is a line of code to get the value of our spinner from its control variable.

time = spin1_text.get()

When we slide, the display will show a voltage for the scale slider and a time for the spinner. The code also uses our Checkbox. The user must turn on the control panel before anything interesting happens. Build and run:
Screenshot 2014-05-02 16.17.11

We left one line in sliderChanged() commented out, a function for the radio buttons to change colors. Add this as the first function in our grouping of functions.

def changeColors():
    color = radio_value.get()
    message1.configure(background = color)
    if color == 'red':
        message1.configure(foreground = 'yellow')
    elif color == 'blue':
        message1.configure(foreground = 'white')
    else:
        message1.configure(foreground = 'black')

This function sets the background color by the radio button and adjusts the foreground color to contrast. Remove the comment from sliderChanged(). Replace the okPressed() function with this to tie everything together:

def okPressed():
    if(isOn.get() == 1):
        label1_text.set('Running...')
        changeColors()
        voltage = scale1_text.get()
        time = spin1_text.get()
        mystring = 'Power Panel!! \n Voltage at: '+ voltage+ 'V\n Time at: ' + time
        message1_text.set(mystring)
    else:
        label1_text.set('Turn system on')
        message1_text.set('Standby Mode')

Build and run:

Screenshot 2014-05-02 16.17.36

We have now covered most of the simple controls. There are some that are more complex such as scroll boxes, drop downs, and menu bars. Before we cover those, our next step is to show a more advanced but consistent way to do everything we just did — with style.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s