0% found this document useful (0 votes)
20 views

Tkinter Guide

This document presents an introduction to Tkinter, the Python standard library for developing graphical interfaces. Explains how to install Tkinter, create your first application with a window and a button, and object-target it. It also shows how to get information about a window, such as its size and position, and insert it into a text box by pressing a button.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views

Tkinter Guide

This document presents an introduction to Tkinter, the Python standard library for developing graphical interfaces. Explains how to install Tkinter, create your first application with a window and a button, and object-target it. It also shows how to get information about a window, such as its size and position, and insert it into a text box by pressing a button.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 51

Index

Tkinter Basics

Graphical interfaces in Python

• Introduction
• Check the version of Tkinter
• Install Tkinter
• The first application
• The first object-oriented application
• Get information from a window

Designing viewports

• Introduction
• The Geometry Manager Pack
• The Grid geometry manager
• The Place geometry manager

Types of windows

• Application and dialog windows


• Modal and non-modal windows

Control variables

• Declare control variables


• set() method
• get() method
• trace() method
• Strategies to validate and calculate data

Menus, toolbars and status bars

• Introduction
• Options menus
• Toolbars
• Status bar
• PyRemoto, an implementation example

Tkinter: graphical interfaces


in Python
Introduction

With Python there are many possibilities to program a graphical user interface ( GUI ) but Tkinter is easy to use, it
is cross-platform and, in addition, it comes included with Python in its version for Windows, for Mac and for most
GNU/Linux distributions . It is considered the de facto standard in GUI programming with Python.

Tkinter is a binding of the Tcl / Tk library that is also available for other languages such as Perl and Ruby.

Despite its long history, its use is not very widespread among personal computer users because its visual
integration with operating systems was not good and it provided few widgets (controls). for
build the programs graphics.

However, starting with TKinter 8.5 the situation took a 180 degree turn in terms of visual integration, improving
significantly in this aspect; also in the number of widgets that are included and in the possibility of working with
styles and themes, which now allow you to fully customize the aesthetics of a program. Therefore, Tkinter is now
an attractive alternative and as recommended as others.

This tutorial is primarily intended to introduce the developer who is not familiar with GUI programming in Tkinter. To
do this, we will follow a series of examples that show, progressively, the use of the elements that are necessary to
build a graphical application: windows, geometry managers, widgets, menus, event management, fonts, styles and
themes. All at cruising speed. For the impatient.

Check the version of Tkinter

Normally, the Tkinter package will be available in our Python installation, except in some GNU/Linux distributions.
To check the version of Tkinter installed there are several possibilities:

1) Start the Python interactive environment and enter:

>>> import tkinter


>>> tkinter.Tcl().eval('info patchlevel')

2) Yeah have h installer Pip introduce:


e
$ pip3 show tkinter

Install Tkinter

If in our Python installation, on a GNU/Linux computer, the Tkinter package is not found, install with:

sudo apt-get install python3-tk

(On other platforms, when Python is installed, the Tkinter modules are also included).

The first application with Tkinter

The following example creates an application that includes a window with a button at the bottom. When you press
the button the application ends its execution. A window is the fundamental element of a GUI application. It is the
first object that is created and the rest of the objects called widgets (labels, buttons, etc.) are placed on top of it.
etc.).

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# The next two lines are necessary to do


# The Tkinter interface is compatible with programs based
# in versions prior to 8.5, with the most recent ones. from tkinter import * # Load tk module (standard widgets)
from tkinter import ttk # Load ttk (for new 8.5+ widgets)

# Defines the main application window

root = Tk ()

# Defines the dimensions of the window, which will be located in


# the center of the screen. If this line is omitted the
# window will adapt to the widgets that are placed in
# she.

root . geometry ( '300x200' ) # width x height

# Assigns a background color to the window. If omitted


# this line the background will be gray

root . configure ( bg = 'beige' )

# Give the window a title

root . title ( 'Application' )

# Defines a button at the bottom of the window


# which when pressed will cause the program to end.
# The first parameter indicates the name of the 'root' window
# where the button will be located

ttk . Button ( root , text = 'Exit' , command = quit ). pack ( side = BOTTOM )

# After defining the main window and a button widget


# the following line will cause when the program is run
# build and display the window, waiting for
# for someone to interact with it.

# If the person presses the Close 'X' button, or # the 'Exit' button, the program will end.

root . mainloop ()

The first object-oriented application

Below is the same application but object-oriented . Although this type of programming is always recommended with
Python, it is not essential. However, if we are going to work with Tkinter, it is
Applications.

From then,

all go to be advantages.
more appropriate, above all, because it facilitates the management of widgets and events that occur in the

Normally, when a graphical application is executed, it waits for a person to interact with it, to press a button, write
something in a text box, select an option from a menu, place the mouse in a certain position, etc., or, an event
occurs in which there is no human intervention, such as a process ending, the value of a variable changing, etc. In
any of these cases, the usual thing will be to link these events or events with some actions to be carried out, which
can be better implemented with the techniques of object-oriented programming.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk

# Create a Python class to define the user interface of


# the application. When creating an object of type 'Application'
# The __init__() method will be automatically executed, which
# builds and displays the window with all its widgets:

class Application ():


def __init__ ( self ):
root = Tk ()
root . geometry ( '300x200' ) root . configure ( bg = 'beige' ) root . title ( 'Application' )
ttk . Button ( root , text = 'Exit' ,
command = root . destroy ). pack ( side = BOTTOM ) root . mainloop ()

# Defines the main() function, which is actually what it indicates


# the beginning of the program. Within it the object is created
# application 'my_app' based on class 'Application':

def main ():


my_app = Application () return 0

# Through the __name__ attribute we have access to the name of a


# a module. Python uses this attribute when running
# a program to know if the module is executed correctly
# independent (in that case __name__ = '__main__') or is
# imported:

if __name__ == '__main main ()


Get information from a window

To conclude this chapter, an application based on the previous examples is included that serves one purpose,
specifically, to display information related to the window.

To do this, new widgets have been added to the application window: a button labeled "Info" and a box of text
that appears empty.

Also, a method has been included that will be called when the "Info" button is pressed to obtain the information
and insert it in the box of text:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk

# The 'Application' class has grown. The example includes


# new widgets in the __init__() constructor method: One of
# them is the 'Info' button that when pressed will call
# to the 'verinfo' method to display information in the other
# widget, a text box: an event executes an action:

class Application ():


def __init__ ( self ):

# The example uses the prefix 'self' to


# declare some variables associated with the object
# ('my_app') of the class 'Application'. Its use is
# essential so that you can access your
# values from other methods:

self . root = Tk ()
self . root . geometry ( '300x200' )

# Prevents the edges from moving to


# enlarge or reduce the size of the 'self.root' window:

self . root . resizable ( width = False , height = False )


self . root . title ( 'View info' )

# Defines the Text 'self.tinfo' widget in which


# You can enter multiple lines of text:

self . tinfo = Text ( self . root , width = 40 , height = 10 )

# Place the 'self.tinfo' text box in the


# top of the 'self.root' window:

self . tinfo . pack ( side = TOP )

# Defines the Button widget 'self.binfo' that will call


# to the 'self.verinfo' method when pressed

self . binfo = ttk . Button ( self . root , text = 'Info' ,


command = self . verinfo )

# Place the 'self.binfo' button below and to the left


# from the previous widget

self . binfo . pack ( side = LEFT )

# Defines the 'self.bexit' button. In this case


# when pressed, the method will destroy or
# will terminate application-window 'self.root' with
# 'self.root.destroy'

self . bexit = ttk . Button ( self . root , text = 'Exit' ,


command = self . root . destroy )

# Place the 'self.bsexit' button to the right of the


# previous object.

self . bexit . pack ( side = RIGHT )

# The focus of the application is on the button


# 'self.binfo' highlighting its border. If pressed
# the space bar the button that has focus
# will be pressed. Focus can change from a widget
# to another with the tab key [tab]

self . binfo . focus_set ()


self . root . mainloop ()

def verinfo ( self ):

# Delete the contents of the text box at any given time #

self . tinfo . delete ( "1.0" , END )

# Gets information from the 'self.root' window:

info1 = self . root . winfo_class ()


info2 = self . root . winfo_geometry ()
info3 = str ( self . root . winfo_width ())
info4 = str ( self . root . winfo_height ())
info5 = str ( self . root . winfo_rootx ())
info6 = str ( self . root . winfo_rooty ())
info7 = str ( self . root . winfo_id ())
info8 = self . root . winfo_name ()
info9 = self . root . winfo_manager ()

# Build a text string with all the # information obtained:

info_text = "Class of 'root': " + info1 + "\n"


info_text += "Resolution and position: " + info2 + "\n"
info_text += "Window width: " + info3 + "\n" info_text += "Window height: " + info4 + "\n" info_text +=
"Pos. Window X: " + info5 + "\n" info_text += "Pos. Window Y: " + info6 + "\n"
info_text += "Id. of 'root': " + info7 + "\n" info_text += "Object name: " + info8 + "\n" info_text +=
"Window manager: " + info9 + "\n"

# Enter the information in the text box:

self . tinfo . insert ( "1.0" , info_text )

def main ():


my_app = Application ()
return 0

if __name__ == '__main__' :
main ()
In the application, the pack() method is used to place the widgets in a specific position within the window. This
method gives its name to one of the three existing geometry managers in Tkinter, which are responsible for this
task.

Tkinter: Designing viewports

Introduction

Geometry managers are used to define the way widgets (controls) should be placed within a window. In Tkinter
there are three geometry managers: pack , grid and place .

If an application has several windows, each of them can be built with any of these managers, interchangeably. It
will be the developer who will have to choose the one that best solves the design they have. by in
front of in each moment.
Also, indicate that to build the windows you can use special widgets (frames, panels, etc.) that act as containers for
other widgets. These widgets are used to group various controls to make operation easier for users. In the
windows that are used, a manager can be used with the window and a different one to organize the controls within
these widgets.

same

using

each one of they.

Next, we are going to know the characteristics of the three geometric managers and develop a

The application consists of a typical system access window that shows the account of the current computer user in
an input box and presents another box to enter their password. At the bottom there are two buttons: one with the
text 'OK' to validate the password (by calling a method) and another with 'Cancel' to end the application.

The Geometry Manager Pack

below,

right

left.

With this manager, the organization of the widgets is done taking into account the sides of a window: top,

If several controls are located (all) on the top side or (all) on the left side of a window, we will construct a vertical
bar or a horizontal bar of controls. Although it is ideal for simple designs (toolbars, dialog boxes, etc.) it can also be
used with complex designs. Additionally, it is possible to make controls adjust to window size changes.

The example shows the commented application with its window built with the pack manager:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import ttk , font import getpass

# Geometry manager (pack)

class Application ():


def __init__ ( self ):

self . root = Tk ()
self . root . title ( "Access" )

# Change the formatting of the current font to bold to # highlight the two labels that accompany the
input # boxes. (For this change the # 'font' module has been imported at the beginning of the program):

font = font . Font ( weight = 'bold' )

# Defines the labels that accompany the input # boxes and assigns the above font format:

self . tag1 = ttk . Label ( self . root , text = "User:" , font = font )
self . tag2 = ttk . Label ( self . root , text = "Password:" , font = font )

# Declare two string variables to hold # the username and password:

self . user = StringVar () self . key = StringVar ()

# Perform a reading of the username


# logged into the system and assigns it to the
# variable 'self.user' (To capture this
# information has been imported into the getpass module
# at the beginning of the program):

self . user . set ( getpass . getuser ())

# Defines two input boxes that will accept # strings of a maximum length of 30 characters.
# To the first of them 'self.ctext1' which will contain
# the name of the user, the variable is assigned
# 'self.user' to the 'textvariable' option. Any
# The value that the variable takes during the execution of the # program will be reflected in the input
box.
# In the second input box, the one with the password, # the same is done. Additionally, the # 'show'
option is set with a "*" (asterisk) to hide # typing of passwords:

self . ctext1 = ttk . entry ( self . root ,


textvariable = self . user ,
width = 30 )
self . ctext2 = ttk . entry ( self . root ,
textvariable = self . clue ,
width = 30 , show = "*" )
self . separate1 = ttk . Separator ( self . root , orient = HORIZONTAL )

# Two buttons are defined with two methods: The button


# 'Accept' will call the 'self.accept' method when
# be pressed to validate the password; and the button
# 'Cancel' will end the application if it reaches
# press:

self . button1 = ttk . Button ( self . root , text = "OK" ,


command = self . accept )
self . button2 = ttk . Button ( self . root , text = "Cancel" , command = quit )

# Widget positions are defined within


# window. All controls are placed
# towards the side above, except, the last two,
# the buttons, which will be located below the last 'TOP':
# the first button towards the left side and the
# second to your right.
# The possible values for the 'side' option are:
# TOP (top), BOTTOM (bottom), LEFT (left)
# and RIGHT (right). If omitted, the value will be TOP
# The 'fill' option is used to tell the manager
# how to expand/shrink widget if window changes
# of size. It has three possible values: BOTH
# (Horizontally and Vertically), X (Horizontally) and
# And (Vertically). It will work if the option value
# 'expand' is True.
# Finally, the 'padx' and 'pady' options are used
# to add extra external horizontal space and/or
# vertically to the widgets to separate them from each other and from
# the edges of the window. There are other equivalents that
# add extra internal space: 'ipàdx' and 'ipady':

self . tag1 . pack ( side = TOP , fill = BOTH , expand = True ,


padx = 5 , pady = 5 )

self . ctext1 . pack ( side = TOP , fill = X , expand = True , padx = 5 , pady = 5 )
self . tag2 . pack ( side = TOP , fill = BOTH , expand = True , padx = 5 , pady = 5 )
self . ctext2 . pack ( side = TOP , fill = X , expand = True , padx = 5 , pady = 5 )
self . separate1 . pack ( side = TOP , fill = BOTH , expand = True , padx = 5 , pady = 5 )
self . button1 . pack ( side = LEFT , fill = BOTH , expand = True , padx = 5 , pady = 5 )
self . button2 . pack ( side = RIGHT , fill = BOTH , expand = True , padx = 5 , pady = 5 )

# When the program starts, focus # is assigned to the password input box so that # you can start
typing directly:

self . ctext2 . focus_set ()

self . root . mainloop ()

# The 'accept' method is used to validate the


# password entered. Will be called when
# Press the 'OK' button. If the password
# matches string 'tkinter' will be printed
# the 'Access allowed' message and the values
# accepted. Otherwise, the
# 'Access Denied' message and the focus will return to the
# same place.

def accept ( self ):


if self . clue . get () == 'tkinter' : print ( "Access allowed" ) print ( "User: " , self . ctext1 . get ()) print
( "Password:" , self . ctext2 . get ()) else :
print ( "Access Denied" )

# The variable 'self.key' is initialized to


# make the 'self.ctext2' widget clean.
# Finally, focus is reassigned
# to this widget to be able to write a new
# password.

self . clue . set ( "" )


self . ctext2 . focus_set ()

def main ():


my_app = Application ()
return 0

if __name__ == '__main__' : main ()

As we have mentioned before, the application allows you to change the dimension of the window. If we do so, the
widgets will adapt to the new size, taking into account the particular configuration of each one of them. To check
how it works we can drag the edges of the window to enlarge or reduce the size and check how the pack manager
works:
Also, to check how the fill option works we can change the current value X of the self.ctext1.pack widget to Y and
drag the edges of the window. If we drag down the widget it will expand vertically:

The Grid geometry manager

This geometric manager treats a window as if it were a grid, made up of rows and columns like a chess board,
where it is possible to place the widgets using a coordinate (row, column); taking into account that, if required, a
widget can occupy several columns and/or several rows.

With this manager it is possible to build complex windows and make the controls adjust to a new size. Its use is
recommended with layouts in which the controls must appear aligned in several columns or rows, that is, following
the shape of a table.

Grid with non-sizable window

The following example is intended to illustrate how to use the grid manager with a non-resizable window. Also, use
a Frame widget with a 3D effect that will contain the rest of the controls:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk , font import getpass

# Geometry manager (grid). Non-resizable window

class Application ():


def __init__ ( self ):
self . root = Tk ()
self . root . title ( "Access" )

# Establishes that the size of the page cannot be modified.


# window. The resizable(0,0) method is the short form
# of resizable(width=False,height=False).

self . root . resizable ( 0 , 0 )


font = font . Font ( weight = 'bold' )

# Defines a 'Frame' type widget that will be the


# container for the rest of the widgets. The frame will be placed

# in the 'self.root' window, occupying its entire extension.


# The frame is defined with a 2 pixel border and the
# option 'relief' with the value 'raised' adds
# a 3D effect to its edge.
# The 'relief' option allows the following values:
# FLAT, RAISED, SUNKEN,
# GROOVE (groove) and RIDGE (raised edge).
# The 'padding' option adds extra interior space for
# so that the widgets do not get stuck to the edge of the frame.

self . frame = ttk . Frame ( self . root , borderwidth = 2 , relief = "raised" , padding =( 10 , 10 ))

# Defines the rest of the widgets but in this case the first one
# parameter indicates that they will be placed in the widget of the
# previous frame 'self.frame'.

self . tag1 = ttk . Label ( self . frame , text = "User:" , font = font , padding =( 5 , 5 ))
self . tag2 = ttk . Label ( self . frame , text = "Password:" , font = font , padding =( 5 , 5 ))

# Defines variables for the 'textvariable' options of


# each entry box 'ttk.Entry()'.

self . user = StringVar ()


self . key = StringVar ()
self . user . set ( getpass . getuser ())
self . ctext1 = ttk . Entry ( self . frame , textvariable = self . user ,
width = 30 )
self . ctext2 = ttk . Entry ( self . frame , textvariable = self . key ,
show = "*" ,
width = 30 )
self . separate1 = ttk . Separator ( self . frame , orient = HORIZONTAL )
self . button1 = ttk . Button ( self . frame , text = "OK" ,
padding =( 5 , 5 ), command = self . accept )
self . button2 = ttk . Button ( self . frame , text = "Cancel" , padding =( 5 , 5 ), command = quit )

# Defines the location of each widget in the grid.


# In this example there are actually two grids:
# A 1fx1c grid found in the window
# that will occupy the Frame; and another in the 5fx3c Frame for

# the rest of the controls.


# The first row and first column will be number 0.
# The 'column' option indicates the column number and the # 'row' option indicates the row number
where a widget should # be placed.
# The 'columnspan' option tells the manager that the
# widget will occupy a total of a certain number of
# columns. The input boxes 'self.ctext1' and
# 'self.ctext2' will occupy two columns and the bar
# separation 'self.separ1' three.

self . frame . grid ( columns = 0 , row = 0 ) self . tag1 . grid ( columns = 0 , row = 0 ) self . ctext1 . grid
( columns = 1 , row = 0 , columnspan = 2 ) self . tag2 . grid ( column = 0 , row = 1 )
self . ctext2 . grid ( columns = 1 , row = 1 , columnspan = 2 )
self . separate1 . grid ( columns = 0 , row = 3 , columnspan = 3 ) self . button1 . grid ( column = 1 , row
=4)
self . button2 . grid ( column = 2 , row = 4 )

# Sets focus to the # password input box.

self . ctext2 . focus_set ()


self . root . mainloop ()

def accept ( self ):


if self . clue . get () == 'tkinter' : print ( "Access allowed" ) print ( "User: " , self . ctext1 . get ()) print
( "Password: " , self . ctext2 . get ())
else :
print ( "Access Denied" ) self . clue . set ( "" ) self . ctext2 . focus_set ()
def main ():
my_app = Application ()
return 0

if __name__ == '__main__' :
main ()

Grid with resizable window

Next, the application is implemented with a grid with the possibility of adapting the widgets to the window space,
when it changes size:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk , font
import getpass

# Geometry manager (grid). Resizable window

class Application ():


def __init__ ( self ):
self . root = Tk ()
self . root . title ( "Access" )
font = font . Font ( weight = 'bold' )
self . frame = ttk . Frame ( self . root , borderwidth = 2 ,
relief = "raised" , padding =( 10 , 10 ))
self . tag1 = ttk . Label ( self . frame , text = "User:" ,
font = font , padding =( 5 , 5 ))
self . tag2 = ttk . Label ( self . frame , text = "Password:" ,
font = font , padding =( 5 , 5 ))
self . user = StringVar ()
self . key = StringVar ()
self . user . set ( getpass . getuser ())
self . ctext1 = ttk . Entry ( self . frame , textvariable = self . user ,
width = 30 )
self . ctext2 = ttk . Entry ( self . frame , textvariable = self . key ,
show = "*" , width = 30 )
self . separate1 = ttk . Separator ( self . frame , orient = HORIZONTAL )
self . button1 = ttk . Button ( self . frame , text = "OK" ,
padding =( 5 , 5 ), command = self . accept )
self . button2 = ttk . Button ( self . frame , text = "Cancel" , padding =( 5 , 5 ), command = quit )

# To get the grid and widgets to


# adapt to the container, if the size is enlarged or reduced
# of the window, it is necessary to define the 'sticky' option.
# When a widget is placed in the grid it is placed in the
# center of its cell or box. With 'sticky'
# establishes the 'sticky' behavior that the
# widget inside your cell, when you modify the
# window dimension. For this, they are used to
# express their values the cardinal points: N (North),
# S (South), (E) East and (W) West, which can even be
# use in combination. The widget will remain
# 'stuck' to the sides of your cell in the directions
# that are indicated. when the window changes size.
# But defining the 'sticky' option is not enough:
# You have to activate this property later.

self . frame . grid ( column = 0 , row = 0 , padx = 5 , pady = 5 , sticky =( N , S , E , W ))


self . tag1 . grid ( column = 0 , row = 0 ,
sticky =( N , S , E , W ))
self . ctext1 . grid ( columns = 1 , row = 0 , columnspan = 2 ,
sticky =( E , W ))
self . tag2 . grid ( column = 0 , row = 1 ,
sticky =( N , S , E , W ))
self . ctext2 . grid ( columns = 1 , row = 1 , columnspan = 2 ,
sticky =( E , W ))
self . separate1 . grid ( column = 0 , row = 3 , columnspan = 3 , pady = 5 , sticky =( N , S , E , W ))
self . button1 . grid ( columns = 1 , row = 4 , padx = 5 ,
sticky =( E ))
self . button2 . grid ( column = 2 , row = 4 , padx = 5 , sticky =( W ))

# Then the expand property is activated


# or collapse defined before with the option
# 'sticky' of the grid() method.
# Activation is done by containers and by rows
# and columns by assigning a weight to the 'weight' option.
# This option assigns a (relative) weight that is used
# to distribute the extra space between columns
# and/or rows. When the window is expanded, a column # or row with a weight of 2 will grow twice as
fast
# than a column (or row) with weight 1. The value
# Default is 0 which means the column or # or row will not grow at all.
# The usual thing is to assign weights to rows or columns where # there are cells with widgets.

self . root . columnconfigure ( 0 , weight = 1 ) self . root . rowconfigure ( 0 , weight = 1 ) self . frame .
columnconfigure ( 0 , weight = 1 ) self . frame . columnconfigure ( 1 , weight = 1 ) self . frame .
columnconfigure ( 2 , weight = 1 ) self . frame . rowconfigure ( 0 , weight = 1 ) self . frame .
rowconfigure ( 1 , weight = 1 ) self . frame . rowconfigure ( 4 , weight = 1 )

# Sets focus to the # password input box.

self . ctext2 . focus_set ()


self . root . mainloop ()

def accept ( self ):


if self . clue . get () == 'tkinter' :
print ( "Access allowed" )
print ( "User: " , self . ctext1 . get ()) print ( "Password: " , self . ctext2 . get ()) else :
print ( "Access Denied" ) self . clue . set ( "" ) self . ctext2 . focus_set ()

def main ():


my_app = Application ()
return 0

if __name__ == '__main__' :
main ()
After running the application, if we expand the size of the window we can check how the controls adjust to the new
available space according to the directions described in each sticky option:

Also, to see how the weights work, we will change the weight that is assigned to row 4 , which is where the '
Accept ' and ' Cancel ' buttons are located, with the value 5 to multiply by 5 the space to be added in this row
when the window expands:

The Place geometry manager

This manager is the easiest to use because it is based on absolute positioning to place the widgets, although the
work of "calculating" the position of each widget is usually quite laborious. We know that a window has a certain
width and height (usually measured in pixels). Well, with this method to place a widget we will simply have to
choose the coordinate (x,y) of its expressed location in pixels.

The position (x=0, y=0) is located in the upper-left corner of the window.

With this manager the size and position of a widget will not change when modifying the dimensions of a window.

Finally, we show the famous application made with the place geometry manager. In this case, the way to display
the validation message is using a label that changes color depending on whether the password is correct or not.
Also, it uses an additional method to "clean up" the error message when the mouse is clicked on the password
input box. The widget event is associated with the method using the bind() method.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk , font
import getpass

# Geometry manager (place)

class Application ():


def __init__ ( self ):
self . root = Tk ()

# Defines the window dimension

self . root . geometry ( "430x200" )

# Sets that you cannot change the size of the

# window

self . root . resizable ( 0 , 0 )


self . root . title ( "Access" )
self . font = font . Font ( weight = 'bold' )
self . tag1 = ttk . Label ( self . root , text = "User:" , font = self . font )
self . tag2 = ttk . Label ( self . root , text = "Password:" , font = self . font )

# Declares a string variable that is assigned to


# the 'textvariable' option of a 'Label' widget to
# show messages in the window. Color is assigned
# blue to the 'foreground' option for the message.

self . mensa = StringVar ()


self . tag3 = ttk . Label ( self . root , textvariable = self . mensa , font = self . font , foreground = 'blue' )

self . user = StringVar ()


self . key = StringVar ()
self . user . set ( getpass . getuser ())
self . ctext1 = ttk . entry ( self . root ,
textvariable = self . user , width = 30 )
self . ctext2 = ttk . entry ( self . root ,
textvariable = self . clue ,
width = 30 ,
show = "*" )
self . separate1 = ttk . Separator ( self . root , orient = HORIZONTAL )
self . button1 = ttk . Button ( self . root , text = "OK" ,
padding =( 5 , 5 ), command = self . accept )
self . button2 = ttk . Button ( self . root , text = "Cancel" , padding =( 5 , 5 ), command = quit )

# Widget locations are defined in the


# window assigning the values of the 'x' and 'y' options
# in pixels.

self . tag1 . place ( x = 30 , y = 40 )


self . tag2 . place ( x = 30 , y = 80 )
self . tag3 . place ( x = 150 , y = 120 )
self . ctext1 . place ( x = 150 , y = 42 )
self . ctext2 . place ( x = 150 , y = 82 )

self . separate1 . place ( x = 5 , y = 145 , bordermode = OUTSIDE , height = 10 , width = 420 )


self . button1 . place ( x = 170 , y = 160 )
self . button2 . place ( x = 290 , y = 160 ) self . ctext2 . focus_set ()

# The 'bind()' method binds the 'click' event


# with the left mouse button in the input box
# of the password' expressed with '<button-1>' with the
# 'self.delete_mensa' method that deletes the message and the
# password and returns focus to the same control.
# Other examples of actions that can be captured:
# <double-button-1>, <buttonrelease-1>, <enter>, <leave>, # <focusin>, <focusout>, <return>, <shift-
up>, <key-f10>, # <key -space>, <key-print>, <keypress-h>, etc.

self . ctext2 . bind ( '<button-1>' , self . delete_message ) self . root . mainloop ()

# Declare method to validate password and display


# a message in the window itself, using the label
# 'self.mensa'. When the password is correct,
# assigns the color blue to the tag 'self.etiq3' and
# when the color red is incorrect. For it. is used
# the 'configure()' method that allows changing the values
# of the widget options.

def accept ( self ):


if self . clue . get () == 'tkinter' :
self . tag3 . configure ( foreground = 'blue' )
self . mensa . set ( "Access Allowed" )
else :
self . tag3 . configure ( foreground = 'red' )
self . mensa . set ( "Access Denied" )

# Declares a method to delete the previous message and


# password input box

def delete_message ( self , event ):


self . clue . set ( "" )
self . mensa . set ( "" ) def main ():
my_app = Application () return 0

if __name__ == '__main__' : main ()

Tkinter: Types of windows

Application and dialog windows

In the previous entry we discussed the different geometry managers that are used to design the windows of an
application. Next, we are going to explore the different types of windows that we can create and the uses
that have.

In Tkinter there are two types of windows: application windows , which are usually the ones that start and end
graphical applications; and from which the dialog windows are accessed, which together
constitute th Interfac of user.
e e

Both, from a design point of view, are built exactly the same with the managers.
of geometry alr known: pack , grid and place .
ea
The following example shows an application window with an ' Open ' button that each time it is pressed opens a
different child (dialog) window and in a different position. We will observe that the daughter windows that are
generated are always located on a higher plane with respect to those created previously. Furthermore, if we open
several windows we can interact without problems with all of them, change their positions,
close them, etc

In this case, the pack geometry manager is used to create both the application window and the daughters, but we
will notice some differences in the code:

The window of application HE define with


Tk() :

self.root Tk()
Dialog windows (children) are defined with Toplevel() :

self.dialogue = toplevel()

The mainloop() method makes it listen to application window events: self.root.mainloop()

The wait_window() method causes the dialog window to listen to local events while it waits.
be closed:

self.root.wait_window(self.connect)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk

class Application ():


''' Application Class '''

# Declare a class variable to count windows

window = 0

# Declare a class variable to use in # calculating the position of a window

posx_y = 0

def __init__ ( self ):


''' Build application window '''

# Declare application window

self . root = Tk ()
# Defines the dimension of the window 300x200 # that will be located at the coordinate x=500,y=50

self . root . geometry ( '300x200+500+50' )

self . root . resizable ( 0 , 0 )


self . root . title ( "Application Window" )

# Defines 'Open' button that will be used to


# open dialog windows. The button
# is linked with the 'self.open' method

button = ttk . Button ( self . root , text = 'Open' ,


command = self . open ) button . pack ( side = BOTTOM , padx = 20 , pady
= 20 ) self . root . mainloop ()

def open ( self ):


''' Build a dialog window '''

# Defines a new dialog window

self . dialog = Toplevel ()

# Increase the window counter by 1

Application . window += 1

# Recalculate window position

Application . posx_y += 50
tamypos = '200x100+' + str ( Application . posx_y )+ \ '+' + str ( Application . posx_y )
self . dialogue . geometry ( tamypos ) self . dialogue . resizable ( 0 , 0 )

# Get identifier of the new window

ident = self . dialogue . winfo_id ()

# Build title bar message

title = str ( Application . window )+ ": " + str ( ident )


self . dialogue . title

# Defines the 'Close' button that whenever


# pressed will close (destroy) the window
# 'self.dialog' calling method
# 'self.dialogue.destroy'
button = ttk . Button ( self . dialog , text = 'Close' ,
command = self . dialogue . destroy )
button . pack ( side = BOTTOM , padx = 20 , pady = 20 )

# When the program execution reaches this


# point the wait_window() method is used to
# wait for the 'self.dialog' window to be
# destroyed.
# Meanwhile, local events are attended to.
# occur, so other parts of the
# application will continue to function normally.
# If there is code after this line it will be executed # when the 'self.dialog' window is closed.

self . root . wait_window ( self . dialog )

def main ():


my_app = Application ()
return ( 0 )

if __name__ == '__main__' :
main ()

Modal and non-modal windows

The child windows in the previous example are of the non-modal type because while they exist it is possible to
interact freely with them, without any limit, except that if we close the main window all the windows will be closed.
daughters open.
An obvious example that uses non-modal windows is in the most well-known office applications, which allow
working with several documents at the same time, each of them open in its own window, allowing the user to
switch without restrictions from one window to another.

The opposite case is that of modal windows . When a modal window is open, it will not be possible to interact with
other application windows until it is closed.

A typical example is some dialog windows used to set application preferences, which require them to be closed
before others are allowed to open.

To demonstrate this, we use the following example in which it is only possible to keep only one window open
daughter, although the we close we will can open othe
Yeah r.
The grab_set() method is used to create the modal window and the transiet() method is used to convert the dialog
window to a transient window , causing it to be hidden when the application window is minimized.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk

class Application ():


window = 0
posx_y = 0

def __init__ ( self ):


self . root = Tk ()
self . root . geometry ( '300x200+500+50' )
self . root . resizable ( 0 , 0 )
self . root . title ( "Application Window" ) button = ttk . Button ( self . root , text = 'Open' ,
command = self . open )
button . pack ( side = BOTTOM , padx = 20 , pady = 20 ) self . root . mainloop ()

def open ( self ):


''' Build a dialog window '''
self . dialog = Toplevel ()
Application . window += 1
Application . posx_y += 50
tamypos = '200x100+' + str ( Application . posx_y )+ \
'+' + str ( Application . posx_y )
self . dialogue . geometry ( tamypos ) self . dialogue . resizable ( 0 , 0 ) ident = self . dialogue . winfo_id
() title = str ( Application . window )+ ": " + str ( ident ) self . dialogue . title
button = ttk . Button ( self . dialog , text = 'Close' ,
command = self . dialogue . destroy )
button . pack ( side = BOTTOM , padx = 20 , pady = 20 )

# Converts the 'self.dialog' window to


# transient with respect to its master window
# 'self.root'.
# A transitional window is always drawn on
# his teacher and will hide when the teacher is
# minimized. If the 'master' argument is
# omitted value, by default, it will be the window
# mother.

self . dialogue . transient ( master = self . root )

# The grab_set() method ensures that there are no events


# of mouse or keyboard that are sent to another window
# different from 'self.dialog'. Is used for
# create a modal window that will be
# necessary to close to be able to work with another
# different. This also prevents the
# same window is opened several times.
self . root . wait_window ( self . dialog )

def main ():


my_app = Application ()
return ( 0 )

if __name__ == '__main__' : main ()

Control variables in Tkinter

Control variables

Control variables are special objects that are associated with widgets to store their values and make them available in
other parts of the program. They can be of numeric, string and boolean type.

When a control variable changes value, the widget that uses it automatically reflects this, and vice versa.

Control variables are also used to connect multiple widgets of the same type, for example, multiple Radiobutton
controls. In this case they will take one of several possible values.

Declare control variables

Control variables are declared differently depending on the type of data they store: integer = IntVar () # Declare
variable of type integer float = DoubleVar () # Declare variable of type float string = StringVar () # Declare variable of
type string boolean = BooleanVar () # Declare variable of type boolean

Also, when declaring a variable it is possible to assign an initial value: blog = StringVar ( value = "Python for
impatients" )
set() method

The set() method assigns a value to a control variable. Used to modify the value or state of a widget: name =
StringVar ()
art_id = IntVar ()
name . set ( "Python for the impatient" )
art_id . set ( 1 )
blog = ttk . Entry ( window , textvariable = name , width = 25 ) arti = ttk . Label ( window , textvariable = art_id )

get() method

The get() method obtains the value that a control variable has at a given time. Used when it is necessary to read the
value of a control: print ( 'Blog:' , name . get ())

print ( 'Article id:' , art_id . get ())

trace() method

The trace() method is used to "detect" when a variable is read, changes value or is deleted:

widget.trace(type, function)

The first argument sets the type of event to check: ' r ' variable read, ' w ' variable write, and ' u ' variable delete. The
second argument indicates the function that will be called when the event occurs.

In the following example, a control variable of type string is defined and with the trace() method its reading and value
change are associated with two functions that are called when these events occur. Specifically, when the set() method
is used the ' change() ' function will be called and when the get() method is used the ' read() ' function will be called.

def changes (* args ):


print ( "Its value has changed" )

def reads (* args ):


print ( "Its value has been read" )

variable = StringVar () variable . trace ( "w" , change ) variable . trace ( "r" , read ) variable . set ( "Hello" ) print
( variable . get ()) print ( variable . get ())

Strategies to validate and calculate data

When building a window with several widgets, different strategies can be followed to validate the data entered
during the execution of a program:
• One possible option could validate the information and perform the calculations after it is entered, for example,
after a button is pressed.

• Another possibility could be using the trace() method and the ' command ' option, to validate and calculate the
information just at the moment that a widget and its associated variable change value.

Below is the same practical case using both techniques.

The program in this example calculates the cost of a train trip taking into account the number of passengers; the
type of ticket (one-way or round-trip); the class in which you travel (which can be economy, first or luxury class);
the distance in kilometers and the price per kilometer (by default it is 0.10 euro cents).

The calculation of the amount to pay is carried out by multiplying the number of travelers by km and price, with the
following increments:

• If the trip is round trip, multiply the total by 1.5

• If the class is first, the total is multiplied by 1.2 and if it is luxury, it is multiplied by 2.
Validation and subsequent calculation

In the first solution, the validation of the data and the calculation of the amount to pay is carried out after pressing
h butto "Calculate"
e n .

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk

# Calculate travel cost with validation and subsequent calculation

class Application ():


def __init__ ( self ):
self . root = Tk ()
self . root . title ( "High Speed" )

# Declare control variables

self . via_num = IntVar ( value = 1 )


self . ida_vue = BooleanVar ()

self . class = StringVar ( value = 't' )


self . km = IntVar ( value = 1 )
self . price = DoubleVar ( value = 0.10 )
self . total = DoubleVar ( value = 0.0 )

# Load image to associate with Label() widget

train = PhotoImage ( file = 'train-128x64.png' )


# Declare window widgets
# The 'Calculate' Button type widget is included that uses
# the 'command' option to validate data and calculate the
# amount to pay when pressed

self . image1 = ttk . Label ( self . root , image = train , anchor = "center" )
self . tag1 = ttk . Label ( self . root , text = "Travelers:" )
self . trip = Spinbox ( self . root , from_ = 1 , to = 20 , wrap = True , textvariable = self . num_via , state =
'readonly' )
self . idavue = ttk . Checkbutton ( self . root , text = 'Round Trip' , variable = self . ida_vue , onvalue = True
, offvalue = False )
self . tag2 = ttk . Label ( self . root , text = "Class:" )
self . class1 = ttk . Radiobutton ( self . root , text = 'Tourist' ,
variable = self . class , value = 't' )
self . class2 = ttk . Radiobutton ( self . root , text = 'First' ,
variable = self . class , value = 'p' )
self . class3 = ttk . Radiobutton ( self . root , text = 'Luxury' ,
variable = self . class , value = 'l' )
self . tag3 = ttk . Label ( self . root , text = "Distance Kilometers):" )
self . dist = ttk . Entry ( self . root , textvariable = self . km , width = 10 )
self . tag4 = ttk . Label ( self . root , text = "Price:" )
self . cost = ttk . Entry ( self . root , textvariable = self . price , width = 10 )
self . tag5 = ttk . Label ( self . root , text = "To Pay (euros):" )
self . tag6 = ttk . Label ( self . root , textvariable = self . total , foreground = "yellow" , background = "black"
, borderwidth = 5 , anchor = "e" )
self . separate1 = ttk . Separator ( self . root , orient = HORIZONTAL )

self . button1 = ttk . Button ( self . root , text = "Calculate" , command = self . calculate )
self . button2 = ttk . Button ( self . root , text = "Exit" , command = quit )

self . image 1 . pack ( side = TOP , fill = BOTH , expand = True , padx = 10 , pady = 5 )
self . tag1 . pack ( side = TOP , fill = BOTH , expand = True , padx = 10 , pady = 5 )
self . journey . pack ( side = TOP , fill = X , expand = True , padx = 20 , pady = 5 )
self . idavue . pack ( side = TOP , fill = X , expand = True , padx = 20 , pady = 5 )
self . tag2 . pack ( side = TOP , fill = BOTH , expand = True , padx = 10 , pady = 5 )
self . class1 . pack ( side = TOP , fill = BOTH , expand = True , padx = 20 , pady = 5 )
self . class2 . pack ( side = TOP , fill = BOTH , expand = True , padx = 20 , pady = 5 )
self . class3 . pack ( side = TOP , fill = BOTH , expand = True , padx = 20 , pady = 5 )
self . tag3 . pack ( side = TOP , fill = BOTH , expand = True , padx = 10 , pady = 5 )
self . dist . pack ( side = TOP , fill = X , expand = True , padx = 20 , pady = 5 )
self . tag4 . pack ( side = TOP , fill = BOTH , expand = True , padx = 10 , pady = 5 )
self . cost . pack ( side = TOP , fill = X , expand = True , padx = 20 , pady = 5 )
self . tag5 . pack ( side = TOP , fill = BOTH , expand = True , padx = 10 , pady = 5 )
self . tag6 . pack ( side = TOP , fill = BOTH , expand = True , padx = 20 , pady = 5 )
self . separate1 . pack ( side = TOP , fill = BOTH , expand = True , padx = 5 , pady = 5 )
self . button1 . pack ( side = LEFT , fill = BOTH , expand = True , padx = 10 , pady = 10 )
self . button2 . pack ( side = RIGHT , fill = BOTH , expand = True , padx = 10 , pady = 10 )
self . root . mainloop ()

def calculate ( self ):

# Function to validate data and calculate amount to pay

data_error = False
total = 0
try :
km = int ( self . km . get ())
price = float ( self . price . get ())
except :
data_error = True
if not data_error :
total = self . via_num . get () * km * price
if self . ida_vue . get ():
total = total * 1.5
if self . class . get () == 'p' :
total = total * 1.2
elif self . class . get () == 'l' :
total = total * 2
self . total . set ( total )
else :
self . total . set ( "ERROR!" )

def main ():


my_app = Application ()
return 0

if __name__ == '__main__' :
main ()

Validation and immediate calculation

In the second solution the ' Calculate ' button is not used. Validation and calculation are performed by changing the
value of any widget, displaying the result immediately. For the data entry widgets ( Entry() ) two traces are defined to
detect any change in the data and in the rest of the widgets the option is used ' command '.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from tkinter import *


from tkinter import ttk

# Calculate travel cost with validation and immediate calculation

class Application ():


def __init__ ( self ):
self . root = Tk ()
self . root . title ( "High Speed" )

# Declare control variables

self . via_num = IntVar ( value = 1 ) self . ida_vue = BooleanVar () self . class = StringVar ( value = 't' ) self
. km = IntVar ( value = 1 )
self . price = DoubleVar ( value = 0.10 )
self . total = DoubleVar ( value = 0.0 ) # Define traces with control variables from Entry() widgets

# to detect changes in the data. If changes occur


# the 'self.calculate' function is called for validation and to
# calculate amount to pay
self . km . trace ( 'w' , self . calculate )
self . price . trace ( 'w' , self . calculate )

# Call function to validate and calculate

self . calculate ()

# Load image to associate with Label() widget

train = PhotoImage ( file = 'train-128x64.png' )

# Declare window widgets


# In Spinbox, Checkbutton and Radiobutton type widgets
# 'command' option is used to call the function
# 'self.calculate' to validate data and calculate amount to
# pay immediately

self . image1 = ttk . Label ( self . root , image = train , anchor = "center" )
self . tag1 = ttk . Label ( self . root , text = "Travelers:" )
self . trip = Spinbox ( self . root , from_ = 1 , to = 20 , wrap = True , textvariable = self . num_via , state =
'readonly' , command = self . calculate )
self . idavue = ttk . Checkbutton ( self . root , text = 'Round Trip' , variable = self . ida_vue , onvalue = True
, offvalue = False , command = self . calculate )
self . tag2 = ttk . Label ( self . root , text = "Class:" )
self . class1 = ttk . Radiobutton ( self . root , text = 'Tourist' , variable = self . class , value = 't' , command =
self . calculate )
self . class2 = ttk . Radiobutton ( self . root , text = 'First' , variable = self . class , value = 'p' , command =
self . calculate )
self . class3 = ttk . Radiobutton ( self . root , text = 'Luxury' , variable = self . class , value = 'l' , command =
self . calculate )
self . tag3 = ttk . Label ( self . root ,

text = "Distance (Kilometers):" )


self . dist = ttk . Entry ( self . root , textvariable = self . km , width = 10 )
self . tag4 = ttk . Label ( self . root , text = "Price:" )
self . cost = ttk . Entry ( self . root , textvariable = self . price ,
width = 10 )
self . tag5 = ttk . Label ( self . root , text = "To Pay (euros):" )
self . tag6 = ttk . Label ( self . root , textvariable = self . total ,
foreground = "yellow" , background = "black" , borderwidth = 5 , anchor =
"e" )
self . separate1 = ttk . Separator ( self . root , orient = HORIZONTAL )

self . button1 = ttk . Button ( self . root , text = "Exit" , command = quit )
self . image 1 . pack ( side = TOP , fill = BOTH , expand = True , padx = 10 , pady = 5 )
self . tag1 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 10 , pady = 5 )
self . journey . pack ( side = TOP , fill = X , expand = True ,
padx = 20 , pady = 5 )
self . idavue . pack ( side = TOP , fill = X , expand = True , padx = 20 , pady = 5 )
self . tag2 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 10 , pady = 5 )
self . class1 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 20 , pady = 5 )
self . class2 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 20 , pady = 5 )
self . class3 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 20 , pady = 5 )
self . tag3 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 10 , pady = 5 )
self . dist . pack ( side = TOP , fill = X , expand = True , padx = 20 , pady = 5 )
self . tag4 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 10 , pady = 5 )
self . cost . pack ( side = TOP , fill = X , expand = True ,
padx = 20 , pady = 5 )
self . tag5 . pack ( side = TOP , fill = BOTH , expand = True ,
padx = 10 , pady = 5 )
self . tag6 . pack ( side = TOP , fill = BOTH , expand = True , padx = 20 , pady = 5 )

self . separate1 . pack ( side = TOP , fill = BOTH , expand = True , padx = 5 , pady = 5 )
self . button1 . pack ( side = RIGHT , fill = BOTH , expand = True , padx = 10 , pady = 10 )
self . root . mainloop ()

def calculate ( self , * args ):

# Function to validate data and calculate amount to pay

data_error = False
total = 0
try :

km = int ( self . km . get ())


price = float ( self . price . get ())
except :
data_error = True
if not data_error :
total = self . via_num . get () * km * price
if self . ida_vue . get ():
total = total * 1.5
if self . class . get () == 'p' :
total = total * 1.2
elif self . class . get () == 'l' :
total = total * 2
self . total . set ( total )
else :
self . total . set ( "ERROR!" )

def main ():


my_app = Application ()
return 0

if __name__ == '__main__' :
main ()

Menus, toolbars and status bars in Tkinter

Introduction

Tkinter has specific widgets to include option menus of different types in an application. In addition, following some
guidelines in the construction of windows it is possible to add other elements such as bars of tools
and of state .

All of these components are frequently used in interface development because they make it much easier for users to
interact with graphical applications.

Options menus

Menus can be built by grouping several drop-down submenus in a menu bar, using button-based menus, or using
typical contextual menus that change their available options depending on where they are activated in the application
window.
Among the types of options that can be included in a menu are those whose status represents a logical value of
activated or deactivated ( add_checkbutton ); those that allow you to choose an option from among several existing
ones ( add_radiobutton ) and those that directly execute a method or function ( add_command ).

Menu options can include icons and be associated with shortcuts or key combinations that have the same effect as if
they were selected with a mouse click. Also, at a given time, they can be disabled to prevent them from being
selected.

Toolbars

A Tkinter application, in addition to option menus, can incorporate toolbars built from buttons stacked horizontally or
vertically. These toolbars are often located just below the application menu bar and execute the most used processes
in the system.

Status bar

Status bars are frequently used in graphics programs to display information that is useful to users. Normally, they
occupy the last line of the application window and in some cases they can be hidden to leave more space available for
said window.

PyRemoto, an implementation example

Application window

PyRemoto is an example that aims to show how to include menus and bars in a Tkinter-based application.

The main window of this application has a title bar, a menu bar with three drop-down submenus with different types of
options, a context menu that is activated by clicking with the right mouse button anywhere in the window, a of tools
with two buttons with images and a status bar that can be hidden or shown using the " View status bar " option of the
" Options " submenu.
Contextual menu

Submenu "Options"

In the " Options " submenu there is the " Save " option that will be enabled when an option is chosen from among
those available in this submenu; and will be disabled when the " Save " option itself is used, - intended - to save the
preferences selected by the user.
The example application uses images of different sizes that are located in a folder called " image ". These images are
used in the title bar, submenus, toolbar, and in the " About " dialog window of the " Help " submenu. They have been
obtained from the site flaticon.com , which offers collections of images organized by different themes to programmers
and graphic designers.

"About" dialog window

Also, the program includes several key combinations associated with different menu options,
declared with h bind() method.
e

As works?

The application is started by the main() function which checks that all images in the application exist in the " image "
folder. If all the images are available, the application object will be created using the PyRemoto() class. This class has
a __init__() method that builds the application window, menus, toolbar, and a status bar.

The application is incomplete but it teaches how to organize the methods that are called from the different options
of the menus and bars.

Finally, the example includes the " About " dialog window, which is of a modal type, to show how to open this type of
window from a menu option.

#!/usr/bin/env python

# -*- coding: utf-8 -*-


#
# yam: pyremoto.py (Python 3.x).
# description: Remote access to computers via ssh, sftp and rdp
# purpose: Construction of menus, toolbars
# and status
# author: python for the impatient
#
# ----------------------------------------------------------
'''PyRemoto: Remote Access to computers via ssh, sftp and rdp '''

__author__ = 'python for the impatient'


__title__ = 'PyRemote'
__date__ = ''
__version__ = '0.0.1'
__license__ = 'GNU GPLv3'

import os , sys , webbrowser , platform


from tkinter import *
from tkinter import ttk , font , messagebox

# PYREMOTO: REMOTE ACCESS TO EQUIPMENT VIA SSH, SFTP and RDP

class PyRemote ():


''' PyRemote class '''

# DECLARE APPLICATION BUILDER METHOD

def __init__ ( self , folder_img , icons ):


''' Define application window, menu, submenus, context menu, toolbar, status bar and keyboard shortcuts
'''

# INITIALIZE VARIABLES

self . img_folder = img_folder


self . icons = icons

# DEFINE APPLICATION WINDOW:

self . root = Tk ()

# SET APPLICATION WINDOW PROPERTIES: self . root . title ( "PyRemoto " + __version__ ) # Title
self . icon1 = PhotoImage ( file = self . icons [ 0 ]) # App icon self . root . iconphoto ( self . root , self . icon1
) # Assign app icon self . root . option_add ( "*Font" , "Helvetica 12" ) # Default Font self . root . option_add
( '*tearOff' , False ) # Disable floating submenus self . root . attributes ( '-fullscreen' , True ) # Maximize full
window self . root . minsize ( 400 , 300 ) # Set minimum window size

# SET FONT STYLE FOR SOME WIDGETS:

self . font = font . Font ( weight = 'normal' ) # normal, bold, etc...

# DECLARE VARIABLES FOR DEFAULT OPTIONS: # (These values could be read from a file
# setting)

self . CFG_TIPOCONEX = IntVar ()


self . CFG_TIPOCONEX . set ( 1 ) # shh self . CFG_TIPOEMUT = IntVar ()
self . CFG_TIPOEMUT . set ( 1 ) # xterm self . CFG_TYPEEXP = IntVar ()
self . CFG_EXPTYPE . set ( 1 ) # thunar

# DECLARE VARIABLE TO SHOW STATUS BAR:

self . state = IntVar ()


self . state . set ( 1 ) # Show Status Bar

# DEFINE APPLICATION MENU BAR:

barramenu = Menu ( self . root ) self . root [ 'menu' ] = barramenu

# DEFINE SUBMENUS 'Sessions', 'Options' and 'Help':

menu1 = Menu ( barmenu )


self . menu2 = Menu ( barmenu )
menu3 = Menu ( barmenu )
barramenu . add_cascade ( menu = menu1 , label = 'Sessions' ) barramenu . add_cascade ( menu = self .
menu2 , label = 'Options' ) barramenu . add_cascade ( menu = menu3 , label = 'Help' )

# DEFINE SUBMENU 'Sessions':

icon2 = PhotoImage ( file = self . icons [ 1 ])


icon3 = PhotoImage ( file = self . icons [ 2 ])

menu1 . add_command ( label = 'Connect...' , command = self . f_connect , underline = 0 , accelerator =


"Ctrl+c" , image = icon2 , compound = LEFT )
menu1 . add_separator () # Add a separator menu1 . add_command ( label = 'Exit' , command = self .
f_exit , underline = 0 , accelerator = "Ctrl+q" , image = icon3 , compound = LEFT )

# DEFINE SUBMENU 'Options':

self . menu2 . add_checkbutton ( label = 'StatusBar' , variable = self . status , onvalue = 1 , offvalue = 0 ,
command = self . f_verstatus )
self . menu2 . add_separator ()
self . menu2 . add_radiobutton ( label = 'ssh' , variable = self . CFG_CONEXTYPE , command = self .
f_changepc , value = 1 )
self . menu2 . add_radiobutton ( label = 'sftp' , variable = self . CFG_CONEXTYPE , command = self .
f_changepc , value = 2 )
self . menu2 . add_radiobutton ( label = 'rdp' , variable = self . CFG_CONEXTYPE , command = self .
f_changepc , value = 3 )
self . menu2 . add_separator ()
self . menu2 . add_radiobutton ( label = 'xterm' , variable = self . CFG_TIPOEMUT , command = self .
f_changepc , value = 1 )
self . menu2 . add_radiobutton ( label = 'uxterm' , variable = self . CFG_TIPOEMUT , command = self .
f_changeropc , value = 2 )
self . menu2 . add_radiobutton ( label = 'xfce4-terminal' , variable = self . CFG_TIPOEMUT ,
command = self . f_changepc , value = 3 )
self . menu2 . add_separator ()
self . menu2 . add_radiobutton ( label = 'Thunar' ,
variable = self . CFG_TYPEEXP , command = self . f_changepc , value = 1 )
self . menu2 . add_radiobutton ( label = 'Nautilus' , variable = self . CFG_EXPTYPE , command = self .
f_changeropc , value = 2 )
self . menu2 . add_separator ()
self . menu2 . add_command ( label = 'Save' ,
command = self . f_saveoption , state = "disabled" , underline = 0 ,
accelerator = "Ctrl+g" )

# DEFINE SUBMENU 'Help':

menu3 . add_command ( label = 'Web' , command = self . f_web ) menu3 . add_command ( label =
'Keyboard shortcuts' , command = self . f_shortcuts )
icon4 = PhotoImage ( file = self . icons [ 3 ]) menu3 . add_command ( label = "About" , command = self
. f_about , image = icon4 , compound = LEFT )

# DEFINE TOOLBAR:

self . icon5 = PhotoImage ( file = self . icons [ 4 ]) icon6 = PhotoImage ( file = self . icons [ 5 ])

barraherr = Frame ( self . root , relief = RAISED ,


bd = 2 , bg = "#E5E5E5" )
bot1 = Button ( barherr , image = self . icon5 , command = self . f_connect )
bot1 . pack ( side = LEFT , padx = 1 , pady = 1 ) bot2 = Button ( barherr , image = icon6 , command =
self . f_exit )
bot2 . pack ( side = LEFT , padx = 1 , pady = 1 ) barraherr . pack ( side = TOP , fill = X )

# Shows equipment information

info1 = platform . system ()


info2 = platform . do not give ()
info3 = platform . machine ()

# Another way to obtain the information:


# (Not available on some versions of Windows)

# info1 = os.uname().sysname
# info2 = os.uname().nodename
# info3 = os.uname().machine

message = " " + info1 + ": " + info2 + " - " + info3
self . barest = Label ( self . root , text = message ,

# DEFINE STATUS BAR:


bd = 1 , relief = SUNKEN , anchor = W )
self . barraest . pack ( side = BOTTOM , fill = X )

# DEFINE CONTEXTUAL MENU

self . menucontext = Menu ( self . root , tearoff = 0 )


self . menucontext . add_command ( label = "Connect" ,
command = self . f_connect )
self . menucontext . add_command ( label = "Exit" ,
command = self . f_exit )

# DECLARATE HOTKEYS:

self . root . bind ( "<Control-c>" ,


lambda event : self . f_connect ())
self . root . bind ( "<Control-g>" ,
lambda event : self . f_save ())
self . root . bind ( "<Control-q>" ,
lambda event : self . f_exit ())
self . root . bind ( "<Button-3>" ,
self . f_showmenucontext )
self . root . mainloop ()

# DECLARE OTHER APPLICATION METHODS:

def f_saveoption ( self ):


''' If ' Save ' option is enabled, call
method to save configuration options
of the application '''

if self . menu2 . entrycget ( 13 , "state" ) == "normal" : self . menu2 . entryconfig ( 13 , state = "disabled"
) self . f_saveconfig ()

def f_saveconfig ( self ):


''' Save application settings ''' print ( "Saved settings" )

def f_connect ( self ):


''' Define dialog window to connect with computers ''' print ( "Connecting" )

def f_changeropc ( self ):


'''Enable ' Save ' option when choosing any connection type option, terminate emulator or file explorer '''
self . menu2 . entryconfig ( "Save" , state = "normal" )

def f_verstate ( self ):


'''Hide or Show status bar '''
if self . state . get () == 0 :
self . barraest . pack_forget ()
else :
self . barraest . pack ( side = BOTTOM , fill = X )

def f_showmenucontext ( self , e ):


''' Show context menu '''
self . menucontext . post ( e . x_root , e . y_root )

def f_web ( self ):


''' Open web page in Internet browser '''

pag1 = ' http://python-para-impacientes.blogspot.com/'


webbrowser . open_new_tab ( page1 )

def f_shortcuts ( self ):


''' Define dialog window with list of application key combinations '''
pass
def f_about ( self ):
''' Define dialog window ' About ' '''

about = Toplevel ()
about . geometry ( "320x200" )
about . resizable ( width = False , height = False ) about . title ( "About" )
frame1 = ttk . Frame ( about , padding =( 10 , 10 , 10 , 10 ), relief = RAISED )
frame1 . pack ( side = TOP , fill = BOTH , expand = True ) tag1 = Label ( frame1 , image = self . icon5 ,
relief = 'raised' )
tag1 . pack ( side = TOP , padx = 10 , pady = 10 , ipadx = 10 , ipady = 10 )
label2 = Label ( frame1 , text = "PyRemoto " + __version__ , foreground = 'blue' , font = self . font )
tag2 . pack ( side = TOP , padx = 10 )
tag3 = Label ( marco1 , text = "Python for the impatient" )
tag3 . pack ( side = TOP , padx = 10 )
button1 = Button ( frame1 , text = "Exit" , command = about . destroy )
button1 . pack ( side = TOP , padx = 10 , pady = 10 )
button1 . focus_set ()
about . transient ( self . root )
self . root . wait_window ( about )

def f_exit ( self ):


''' Exit the application ''' self . root . destroy ()

# APP FEATURES

def f_check_icons ( icons ):


''' Check existence of icons

icons -- List of ''' icons

for icon in icons :


if not os . path . exists ( icon ):
print ( 'Icon not found:' , icon )
return ( 1 )
return ( 0 )

def main ():


''' Start application '''

# INITIALIZE VARIABLES WITH ROUTES

app_folder = os . getcwd ()
img_folder = app_folder + os . sep + "image" + os . sep

# DECLARE AND VERIFY APP ICONS:


icons = ( img_folder + "pyremoto64x64.png" ,
img_folder + "conec16x16.png" , img_folder + "exit16x16.png" , img_folder + "star16x16.png" ,
img_folder + "conec32x32.png" , img_folder + "exit32x32.png" )
error1 = f_check_icons ( icons )

if not error1 :
my_app = PyRemote ( img_folder , icons )
return ( 0 )

if __name__ == '__main__' :
main ()

You might also like