PHP & More

A technical blog about programming in PHP and about technology in general: programming, workarounds and troubleshooting

Matplotlib is a great python library for plotting and graphics. Graphics include formatted text, text paths and tabular data. Mathematical expressions are a good reason to use Matplotlib for rendering text. Matplotlib also supports some widgets one can use for input. If you use Matplotlib widgets, you vetter know how to size and position them unless – for example – you write a program for yourself.

Following is an example of bad code:

from matplotlib import pyplot as plt
from matplotlib.widgets import TextBox
from matplotlib.widgets import Button

fig,ax=plt.subplots()

teextbox=TextBox(ax,"Label:")
button=Button(ax,'Submit')

plt.show()

The code above create to widgets that fill up the same plotting area inside defined Axes object. In the following image, you can see that both the text entered by the user and the button text overlap. In addition. the text entered by the user exceeds the limit of the plotting area.

Get More Control over Your Widgets

For better results, there are 3 things to do:

  • Use separate plotting area for your widgets.
  • Set the plotting areas’ positions and sizes.
  • Using Event Handlers to control the input length in a TextBox and perform operations.

Separate Plotting Areas

The command plt.subplots() creates a figure, and a single plotting area, a uni-dimensional array of plotting areas or a bi-dimensional array of plotting areas. According to the number of rows and columns. The default is one row and one column. For example:

fig,ax=plt.subplots(nrows=2)

Returns a column of two plotting areas, To set the number of columns use the keyword argument ncols.

Let us see what happens if we set the number of colums (not adding widgets. yrt) by the following code:

from matplotlib import pyplot as plt
from matplotlib.widgets import TextBox
from matplotlib.widgets import Button

fig,ax=plt.subplots(nrows=2)

plt.show()

The code generates two Axes rectangles as follows:

Resizing and Positioning a Plotting Area

The Axes rectangle can be resized and positioned using the function matplotlib.axes.Axes.set_position. One of its arguments can be an array whose members are left,bottom,width and height. The position and size is relative to the figure. :

  • left=0 means that the Axes begin at the left side of the figure
  • left=1 means that the Axes begin at the right side of the figure (which makes them invisible).
  • bottom=0 means that the Axes begin at the bottom of the figure
  • bottom=1 means that the Axes begin at the top of the figure

The following code resizes the Axes rectangles, and adds the widgets:

from matplotlib import pyplot as plt
from matplotlib.widgets import TextBox
from matplotlib.widgets import Button

fig,ax=plt.subplots(nrows=2)

ax[0].set_position([0.2,0.85,0.7,0.08])
ax[1].set_position([0.495,0.6,0.1,0.1])
teextbox=TextBox(ax[0],"Label:",label_pad=0.01,color='cyan',hovercolor='red')
button=Button(ax[1],'Submit')
plt.show()

ax[0] is the rectangle containing the TextBox

ax[1] is the rectangle containing the box. It’s width is 0.1(10% of that of the figure), and its left edge is position at 0.495, which is 0.5+0.1/2. This makes its horizontal alignment centered.

In the following image you’ll see that the background color of the text box is ‘cyan’, and hovercolor defines the background color when the mouse pointer is over the text box.

Setting the Input Text’s Maximal Length and Event Handling

Event handling functions can be attached to widgets. Event handling functions can react to button clicks, text changes, submitions by pressing the Enter key, etc.

If you want to restrict the length of the input text in a TextBox, attach an event handling function as follows:

tb.on_text_change(func)

Where func is a function that gets one argument, the text. In this function, you can restrict the number of character. You better set the cursor position as well, because it increases whenever the user types a character, even if the text is changed by the event handler. Following is an example of how to check that the input matches a pattern all the way:

def tc_func(self,inp):
    if (len(inp)>self.maxlen):
        self.tb.cursor_index=self.curpos
        self.tb.set_val(self.val)
        return
    if (self.decpoint and inp.find('.')<0 and len(inp)&gt;self.maxlen-1):
        self.tb.cursor_index=self.curpos
        self.tb.set_val(self.val)
        return
    if (not self.pattern.match(inp)):
        self.tb.cursor_index=self.curpos
        self.tb.set_val(self.val)
        return
    self.val=inp
    self.curpos=self.tb.cursor_index

From the argument self you can learn that the above function is a member of a class. Wrapping widgets in objects is recommended.

The member ‘cursor_index‘ is the position of the cursor after the event handler finishes its work. set_val sets a new value (or resets it).

The full source from which the code above is taken from my Square Root Calculator found at https://github.com/amity1/SquareRootCalculator

To handle button click events , use the function on_clicked, as follows:

button.on_clicked(click_handler)

The argument passed to the click_handler is an object of type matplotlib.backend_bases.MouseEvent. You can see in the following code how you can learn it:

from matplotlib import pyplot as plt
from matplotlib.widgets import TextBox
from matplotlib.widgets import Button

def click_handler(evt):
    print(type(evt))
    print("Button clicked with:"+ str(evt.button))

fig,ax=plt.subplots(nrows=2)

ax[0].set_position([0.2,0.85,0.7,0.08])
ax[1].set_position([0.495,0.6,0.1,0.1])
teextbox=TextBox(ax[0],"Label:",label_pad=0.01,color='cyan',hovercolor='red')
button=Button(ax[1],'Submit')
button.on_clicked(click_handler)
plt.show()

The event handler above prints the type of mits argument and the mouse button with which the button widget was clicked. Following is the output:

Button clicked with:MouseButton.LEFT

Button clicked with:MouseButton.MIDDLE

Button clicked with:MouseButton.RIGHT
<class 'matplotlib.backend_bases.MouseEvent'&gt;
Button clicked with:MouseButton.LEFT
<class 'matplotlib.backend_bases.MouseEvent'&gt;
Button clicked with:MouseButton.MIDDLE
<class 'matplotlib.backend_bases.MouseEvent'&gt;
Button clicked with:MouseButton.RIGHT

Tables

A table is a widget that can be added to an Axes object in addition to other Artists.

There are two ways to create a table:

If you just choose to create a table without specifying loc, the table location in respect to the Axes, chances are you will not be satisfied.

The following code creates such a default table using the factory:

import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib.widgets import TextBox
from matplotlib.widgets import Button

fig,ax=plt.subplots()
tab=mpl.table.table(ax,cellColours=[['red','green'],['yellow','blue']])

plt.show()

In the following image generated by the code, you will see that the table is created just under the Axes, and it overlaps the frame x-ticks.

You can create the table somewhere else by setting the loc parameter, you can set a cell’s width and height, set a column width automatically, and align text.

Setting The Table’s Location and Modify Cells

To set a table location in respect to the Axes, pass the parameter loc with one of the valid codes, for example:

tab=mpl.table.table(ax,cellText=[['Red','Green'],['Yellow','Blue']],loc='upper left'

A default text alignment in a table cell can be defined by passing the parameter cellLoc when creating a new table. When adding a cell, the argument name is loc. The valid values for loc are: ‘left’, ‘center’ and ‘right’

Accessing a table cell is easy as ABC: access the cells as if the table were a bi-dimensional array whose elements are objects of type matplotlib.table.Cell. For example:

tab[row,col]

You can modify text properties using the function set_text_props of the cell object. And you can change its position and size by modifying properties inherited from class matplotlib.patches.Rectangle.

The following code creates a table near the upper left corner of the Axes, sets the column widths to be automatic, changes the color of text cells, and enlarges one of the cells.

import matplotlib as mpl
from matplotlib import pyplot as plt

fig,ax=plt.subplots()
tab=mpl.table.table(ax,cellText=[['Red','Green'],['Yellow','Blue']],
                    cellColours=[['red','green'],['yellow','blue']],
                    loc='upper left',cellLoc='left' )
tab.auto_set_column_width(0)
tab.auto_set_column_width(1)
tab[1,1].set_height(0.5)
for i,j in ((0,0),(0,1),(1,1)):
    tab[i,j].set_text_props(color='white')

plt.show()

The code above produces the following image:

Adding a Cell

You can add single cells to a table using the function add_cell of the table.

the function should be called with the row number and column number. The caller has to specify the keyword arguments ‘width’ and ‘height’.

The new cell should be connected to the table, and may influence the heights and widths of celles in the same row or column.

The following code adds a cell in a new column and row:

import matplotlib as mpl
from matplotlib import pyplot as plt

fig,ax=plt.subplots()
tab=mpl.table.table(ax,cellText=[['Red','Green'],['Yellow','Blue']],
                    cellColours=[['red','green'],['yellow','blue']],
                    loc='upper left',cellLoc='left')
print ("Table Created")
tab.auto_set_column_width(0)
tab.auto_set_column_width(1)
tab[1,1].set_height(0.5)
for i,j in ((0,0),(0,1),(1,1)):
    tab[i,j].set_text_props(color='white')
tab[1,0].set_xy((0,0.8))
tab.AXESPAD=0
cell=tab.add_cell(2,2,height=0.1,width=0.3,text='New Cell',loc='center',facecolor='orange')
plt.show()

In the following image, you can see a new orange cell that has been added to an existing table:

Advertisements
%d bloggers like this: