# PHP & More

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

The other day, I was invited to a zoom meeting. Zoom is a video conference application also available for computers. It’s address is https://zoom.us.

When the time of our meeting came, I couldn’t hear anything when connecting from my Firefox browser, and saw the following message:

.

After a short IRC conversation on #freebsd@freenode.org, I installed the User-Agent Switcher and Manager add-on on my Firefox.

This add-on adds an icon to the toolbar, Following is the icon as seen in the dark theme:

Now, when you click the icon, you will see the following popup:

Great! Now you can select a browser and operating system using the drop down lists, and then select the browser’s version using one of the radio button. Then, click the “Apply” button to save your changes and enable the add-on. Click the “Test” button to see what browser details will be passed to the remote server.

Good luck!

## GUI Problems? Time to Upgrade Qt

Having upgraded my FreeBSD system and VirtualBox, I found that VirtualBox does not work when invoked from the window manager’s menu. So, I tried running it from the command line, and got the following message:

/usr/local/lib/virtualbox/VirtualBox.so: Undefined symbol “_ZNK6QColor6darkerEi@Qt_5”

I tried to search the web for the cause, and found a link to a compatibility report including added symbols. My symbol was nott found there, but at least I got a clue about what I had to do: upgrade Qt5.

From some reasons I’m not notified that it’s time to upgrade my Qt5.

I found a great option for command pkg:I

pkg version| grep qt5

The output reveals the current version of a package, and if there are later versions; following is a typical output line:

qt5-printsupport-5.12.2 <

I started upgrading Qt5 packages, first qt5-core, then qt5-gui and saw a little progress: new undefined symbols. Finally, VirtualBox started working.

Another application affected by the failure to upgrade Qt5 is VLC , the famous media player from https://www.videolan.org

VLC does not show any error message, and you can run it from the command line with no problems. Specifying the path of the media device or file. For example by typing the command:

vlc cdda:///dev/cd0

if you want to listen to a compact disc.

## Widgets and Tables in Matplotlib

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])
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)>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])
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'>
Button clicked with:MouseButton.LEFT
<class 'matplotlib.backend_bases.MouseEvent'>
Button clicked with:MouseButton.MIDDLE
<class 'matplotlib.backend_bases.MouseEvent'>
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:

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))
plt.show()

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

## VirtualBox Failed to Save Settings

I recently installed VirtualBox configured internet connection to the internet for a virtual windows 7 machine I’ve installed using Vagrant, tried to setup USB, but got the following message: “Failed to save settings – Empty or null HostOnly interface name is not valid.”. Looking at the details, I saw the following “Result Code: E_FAIL (0x80004005) Component: NetworkAdapterWrapper …”.I don’t remember when I started to get that popup message. Couldn’t find any HostOnly adapter using the VirtualBox GUI. Finding the solution in the web is too hard. and I can’t find anything in the user guide either. But, one useful thing I know: in Linux and Unix-like system, I can find configuration files. They are usually found in directories whose names begin with ‘.’. followed by the program’s name or under “${HOME}/.config’ or directories created by the software. This time, the file was found in “${HOME}/VirtualBox VMs/<machine-name>”.
In this case: “\${HOME}/VirtualBox VMs/windows7_default_1563689015423_90469/”. In this directory there are files with the suffix ‘.vbox’. They are XML files. In one of them, named “windows7_default_1563689015423_90469.vbox”, I find the following element:

<DisabledModes>
<InternalNetwork name="intnet"/%gt;
<NATNetwork name="NatNetwork"/>
</DisabledModes>
<HostOnlyInterface name="VirtualBox Host-Only Ethernet Adapter #2"/>

Comment it out.
Problem solved.

## Drawing Simple 3D Shapes in HTML5

HTML5 includes some features that allows developers to draw 3D shapes by drawing bi dimensional shapes and applying 3d transform, such as rotateX, rotateY and rotateZ. For convenience, you can shift the origin of axes using the style property If you don’t want the 3D image too flat (for example, all the faces of a cube having the same size) use perspective and perspective-origin style properties.

You can use the matrix3d style properties instead of the named transforms if, for example, you don’t want to compute angles.

## The Style Properties

A style property can be defined by adding the attribute style to an HTML element, defining a CSS class or accessing a DOM node.

In this section I will explain the properties using a little Javascript program that draws a regular tetrahedron.

Drawing a tetrahedron is done by drawing 4 isosceles triangles and rotating each of them once or twice.

### “perspective” and “perspective-origin”

The distance and angle from which the shape is viewed.

“perspective” holds the distance

“perspective-origin” – a position value.

For example:

var main_div = d3.select('body')
.append('div')
.style('position','absolute')
.style('top','50px')
.style('left','50px')
.style('perspective','50px')
.style('perspective-origin','bottom left');

### Transform Values: “rotateX”, “rotateY”, “rotate” and “transform-origin”

Rotate an axis. Keep the value of the rotated axis coordinate unchanged, and change the rest. The axis is rotated around the position defined by “transform-origin”

The following code adds the data for creating 4 triangles, and rotates 1 triangle 120 degrees to the right and 1 triangle 120 degrees to the left. Rotation is done around the bottom face’s centroid.

main_div.selectAll('div').
data([{color: 'red', transform: null,upperVertexInd: true},
{color: 'black', transform: 'rotateX(90deg)', 'origin':'
100px 100px 0'},
{color: 'blue', transform: 'rotateY(120deg)',origin: cent
roidString,upperVertexInd: true},
{color: 'green', transform: 'rotateY(-120deg)',origin: ce
ntroidString,upperVertexInd: true}])
.enter()
.append('div')
.style('position','absolute')
.style('top',0)
.style('left',0)
.style('transform',d=>d.transform)
.style('transform-origin',d=>d.origin)
.style('transform-style','preserve-3d')

(To be more precise, it rotates the DIV elements)

### Tarnsform Values: “matrix3d”

This matrix is used if you want to use a transformation out of the comfort zone. For example, a rotation transform with cosines and sines of the angle. The argument list contains 16 values, which are the cells of a square matrix of order 4 (4 rows and 4 columns).

This matrix will be applied on (x,y,z,w) vector to get the target vector. When rotating a 2d vector )point), our original z-coordinate will be 0, and w will be 1.

To specify the matrix:

$\left( \begin{matrix} a_0 \ a_4 \ a_8 \ a_{12} \\a_1 \ a_5 \ a_9 \ a_{13} \\a_2 \ a_6 \ a_{10} a_{14} \\a_3 \ a_7 \ a_{11} \ a_{15}\end{matrix} \right)$

use

$matrix3d(a_0,a_1,a_2,...,a_{15})$

In my example, I will rotate 3 triangles, so their top vertex will go to a line perpendicular to the tetrahedron base, and passing through the base’s median.

The median of a triangle is the point where median cross its other, dividing each median at the ratio 1:2.

So, if each side of a triangle is of length 1. The height is $\sqrt(3)\over2$

Since, the height is the length of the median, the distance from a side to the centroid is the height divided by 3, and the requested sine is latex13latex 1 \over 3

The cosine is $\sqrt {1 - {1 \over 3}^2} = {\sqrt 8 \over 3}$

so, we will compute the matrix as follows:

var rotateXCos = Math.sqrt(8) / 3;
var rotateXSin = 1 / 3;
var rotateXMat3d = [1,0,0,0,
0,rotateXCos,rotateXSin,0,
0,-rotateXSin,rotateXCos,0,
0,0,0,1];
var matrixTransformString = 'matrix3d(' + rotateXMat3d + ')';

Now, the code to draw the tetrahedron with *d3.js( is:

var side=100;
var len=100;
var height=side * Math.sqrt(3)/2;
var centroidZValue = -height / 3; // The point where medians meet.
var rotateXCos = Math.sqrt(8) / 3;
var rotateXSin = 1 / 3;
var rotateXMat3d = [1,0,0,0,
0,rotateXCos,rotateXSin,0,
0,-rotateXSin,rotateXCos,0,
0,0,0,1];
var matrixTransformString = 'matrix3d(' + rotateXMat3d + ')';
var centroidString = '150px 0 ' + centroidZValue + 'px';
var main_div = d3.select('body')
.append('div')
.style('position','absolute')
.style('top','50px')
.style('left','50px')
.style('perspective','50px')
.style('perspective-origin','bottom left');
main_div.selectAll('div').
data([{color: 'red', transform: null,upperVertexInd: true},
{color: 'black', transform: 'rotateX(90deg)', 'origin':'
100px 100px 0'},
{color: 'blue', transform: 'rotateY(120deg)',origin: cent
roidString,upperVertexInd: true},
{color: 'green', transform: 'rotateY(-120deg)',origin: ce
ntroidString,upperVertexInd: true}])
.enter()
.append('div')
.style('position','absolute')
.style('top',0)
.style('left',0)
.style('transform',d=>d.transform)
.style('transform-origin',d=>d.origin)
.style('transform-style','preserve-3d')
.append('div')
.style('transform-style','preserve-3d')
.style('position','absolute')
.style('top',0)
.style('left',0)
.style('transform',function(d){
return d.upperVertexInd?matrixTransformString:false;
})
.style('transform-origin',function(d){
return d.upperVertexInd?'0 100px 0':false;
})
.append('svg')
.append('polygon')
.attr('points',[100,100,150,100-height,200,100])
.style('fill','none')
.style('stroke',d=>d.color);

## The Requested Gradient Cannot Be Found: Use the HTML5 Canvas

Some days ago I found that someone was looking for a D3.js expert. To prove one is an expert one has to pass a test, and one of the tasks on this test is to create a 3D color picker with RGB for axes.

The cube faces cannot be filled with a bi-dimensional linear gradients because such gradients are not supported. So, you have to explicitly write a loop to add the pixels.
Using SVG to add the pixels is a bad idea: SVG is an XML language, and uses a DOM tree. Using SVG will use a lot of memory and will slow down your computer. Use a canvas instead. Drawing on a canvas is done by simple Javascript commands, that add lines and shapes.
Following is a little code snippet that fills a cube face:

for (i=x1; i<=x2;i++){
for (j=y1; j<=y2; j++){
rgb_arr[d.rgb_variable[0]]=i;
rgb_arr[d.rgb_variable[1]]=j;
ctx.fillStyle=d3.rgb(rgb_arr[0],rgb_arr[1],rgb_arr[2]);
ctx.fillRect(i,j,1,1);
}
}

Now, to get the color where the mouse points, first get the position using the mouse event’s offsetX and offsetY. These properties will hold the correct value even if the canvas is rotated.
Then you can get the RGBA values of the pixel using method getImageData of the canvas’ context. The method returns the data of a rectangle defined by 4 arguments: x,t,width and height.
Following is an example:

canvas.on('click', function(evt){
var ctx=d3.event.target.getContext('2d');
var pixelData = ctx.getImageData(d3.event.offsetX,d3.event.offsetY,1,1).data;
});

## Thunderbird Calendar Cleanup

The thought to create a copy of my Thunderbird profile did not cross my mind until the last power failure. After each power failure I found that I have to setup my mail account, and that all my events were “lost”.
Wellm the events were not exactly lost, but a new calendar id was created, which is to be used to find all the events and their relevant properties in the calendar extension’s database. The database itself is an SQLite file, and in my system is located under ‘~/.thunderbird/default-dir/calendar-data/local.sqlite’
(Change the default-dir name to your local name, it is by default the one that ends with ‘.default*.

## Finding the Current Calendar Id

The calendar id is the value of the Thunderbird’s user preference ‘calendar.list.sortOrder’. You can view this variables by choosing from the menu:
preferences->preferences->preferences*, and then from the window opend, choosing the Advanced tab, and then clicking the “Config Editor* button.
You can connect to the database using:

sqlite3 /path/to/local.sqlite

From now on, I’ll assume you only have one calendar.

## SQLite Tables

The tables, indexes, trigges and other database entities are stored in a table named ‘sqlite_master’. To find tables, run the query

select name from sqlite_master
where type='table';

For each table column, run the query:

pragma table_info(table_name);

The result will be:

cal_calendar_schema_version
cal_attendees
cal_recurrence
cal_properties
cal_events
cal_todos
cal_tz_version
cal_alarms
cal_relations
cal_attachments

Replace table_name by a table name, for example:

pragma table_info(cal_events)

The result will be:

0|cal_id|TEXT|0||0
1|id|TEXT|0||0
2|time_created|INTEGER|0||0
3|last_modified|INTEGER|0||0
4|title|TEXT|0||0
5|priority|INTEGER|0||0
6|privacy|TEXT|0||0
7|ical_status|TEXT|0||0
8|flags|INTEGER|0||0
9|event_start|INTEGER|0||0
10|event_end|INTEGER|0||0
11|event_stamp|INTEGER|0||0
12|event_start_tz|TEXT|0||0
13|event_end_tz|TEXT|0||0
14|recurrence_id|INTEGER|0||0
15|recurrence_id_tz|TEXT|0||0
16|alarm_last_ack|INTEGER|0||0
17|offline_journal|INTEGER|0||0

## Finding the Last Calendar Id

Now, I guess that an event in the last calendar has the latest event start date.
A good query to find that event can be:

select cal_id, title, event_start
from cal_events
order by event_start;

Let us call the most recent value calendar id “old-cal-id”, and the new calendar id “new-cal-id*

## Updating and Deleting

I suggest that you perform update and delete queries within transaction, so if something goes wrong you can rollback.
Begin a transaction by running the command:

begin transaction

Now, for each table that has the column cal_id, run the query:

update table_name
set cal_id="new-cal-id"
where cal_id="old-cal-id"

Replace table_name by a name of a table that has the column cal_id, for example:

update cal_event
set cal_id="new-cal-id"
where cal_id="old-cal-id";

Now, to delete the rest, run:

delete from table_name
where cal_id != "old-cal-id";

If everything’s fine, it’s time to commit your transaction by running:

COMMIT;