Saturday, July 21, 2012

Tkinter Button Demo

This code is based on the Tcl button.tcl demo, modified to use ttk.Button and its associated ttk.Style.

Four buttons are presented, the panel background is changed to match the selected button's colour name.


The original demo uses the option -highlightbackground  to change the button colour to match the selected background colour; ttk.Button does not have a highlightbackground option, instead, we need to change the ttk.Button style. To ensure that only the demo buttons are changed (and not all buttons), we clone the original button style, TButton, as Demo.TButton; assign it to each button in the demo panel, and then modify it.


On the left is the button appearance if the button background is not modified to match the selected demo panel background; on the right, the button`s appearance once it`s background has been set by modifying it`s associated style.

# File: buttons.py
# References:
#    Passing args to 'command' - http://tkinter.unpythonic.net/wiki/CallbackConfusion
#    Ttk Styling - http://docs.python.org/py3k/library/tkinter.ttk.html?highlight=ttk#tkinter.ttk
#    Copying a style - http://www.tkdocs.com/tutorial/styles.html
#    Available colours - http://www.tcl.tk/man/tcl8.5/TkCmd/colors.htm

from tkinter import *
from tkinter import ttk
from demopanels import MsgPanel, SeeDismissPanel

class ButtonDemo(ttk.Frame):

    def __init__(self, isapp=True, name='buttondemo'):
        ttk.Frame.__init__(self, name=name)
        self.pack(expand=Y, fill=BOTH)
        self.master.title('Button Demo')
        self.isapp = isapp
        self._create_widgets()

    def _create_widgets(self):
        if self.isapp:
            MsgPanel(self, 
                     ["If you click on any of the four buttons below, the background of the ",
                      "button area will change to the colour indicated in the button. ",
                      "You can press Tab to move among the buttons, then press Space ",
                      "to invoke the current button."])
            
            SeeDismissPanel(self)
        
        self._create_demo_panel()
            
    def _create_demo_panel(self):
        demoPanel = Frame(self)
        demoPanel.pack(expand=Y, fill=BOTH, padx=25)
                
        colors = ['Peach Puff', 'Light Blue', 'Sea Green', 'Yellow']
        buttons = []
        for i in range(len(colors)):
            buttons.append(ttk.Button(demoPanel, width=10))
            buttons[i]['text'] = colors[i]
            
            # 'i=i' required, otherwise only the LAST colour value is remembered
            buttons[i]['command'] = ( lambda w=buttons[i], i=i, f=demoPanel: 
                                         self._color_refresh(w, f))
            
            # set style to a clone of default button style
            # if you comment this out, the button background stays gray
            buttons[i]['style'] = 'Demo.TButton'
            
            buttons[i].pack(side=TOP, expand=Y, pady=2)
        
    def _color_refresh(self, widget, frame):     
        # colour to use matches the button text
        color = widget.cget('text')
        
        # change the demo panel frame background colour
        frame['background'] = color
        
        # change background colour of buttons using the 'Demo.TButton'
        # style ('See Code' and 'Dismiss' buttons are not affected)
        ttk.Style().configure('Demo.TButton', background=color)
        
if __name__ == '__main__':
    ButtonDemo().mainloop()