Using Vpython
=============
.. sidebar:: Information on the Web
If an example starts with `import visual` it is the older version 6 and
will need conversion before using.
First install vpython (version 7), simply use ``pip install vpython``.
Vpython runs directly on a web page - it should work on a local web page
generated on your default browser (address something like
"http://localhost:59612").
.. figure:: ../figures/ball_bounce.png
:width: 308
:height: 244
:alt: vpython bouncing ball
Simple demonstration of Vpython
.. code-block:: python
:emphasize-lines: 3,4
from vpython import *
ball = sphere(pos=vector(0,10,0), radius=1, color=color.red)
floor = box(pos=vector(0,0,0), size = vector(10,0.5,10), color=color.green)
ball.velocity=vector(0,0,0)
dt=0.01
t=0
g=-9.81
while t<20:
rate(100)
ball.velocity.y=ball.velocity.y+g*dt
ball.pos=ball.pos+ball.velocity*dt
if (ball.pos.y`_ .
Vpython has quite a few interesting features, such as making graphs.
.. code-block:: python
:emphasize-lines: 2,3
from vpython import *
gd=graph(x=400,y=500,width=600,height=600,background=color.white,
foreground=color.black,title='Graph Tutorial',xtitle='my x-axis',
ytitle='my y-axis',fast=True)
f1=gcurve(color=color.red) # gdots instead of gcurve plots single points
t=0
dt=0.1
while (t<20):
rate(100)
f1.plot(pos=(t,sin(t)))
t=t+dt
The plot has essentially been created in one extended line. Information on
Vpython is given at the following source `"VPythonDocs" `_ .
Find graph information at `Work with 3D objects>Graphs`. In the example
above try changing `fast=False` to the plot definition. Notice what happens
when you move the mouse over the curve.
When plotting values from the Arduino it is often useful to have a scrolling
graph.
.. code-block:: python
from vpython import *
gd=graph(xmin=0,xmax=10,width=600,height=600,
ftitle='Scrolling Graph Tutorial',xtitle='my x-axis',
ytitle='my y-axis',fast=False,scroll=True)
f1=gcurve(color=color.red)
for t in arange(0,30,0.1):
rate(100)
f1.plot(pos=(t,sin(t)))
Note how the script has been altered, we needed to substitute ``xmin=0,xmax=10``
for ``x=400``, add ``scroll=True`` and deleted ``y=500``. Using the built-in
function `arange` we have been able to simplify the logic, and with ``fast=
False`` option, we can view the first part of the plot. Move the mouse
cursor to the x-axis values, (the cursor changes from a single arrow to a
horizontal double arrow), now hold the left button down and move the mouse
leftwards to the first part of the plot.
``graph()`` contains the general data on the graph such as size, scrolling
or not and titles, whereas ``gcurve()`` has specific plotting data such as
color and label name (2 or more curves in one plot). plot() is how the plot
is generated, in the example we are plotting time against a sin curve(x and
y), in a live Arduino session this would be normally time against a read-in
value.
We left Python Fundamentals with an unresolved python output to Two Light
Detecting Resistors. Apart from proving that it works with the Arduino
Serial Monitor and Serial Plotter now is a good time to provide a solution.
We connect the Arduino to the USB and start up the following Python script,
which will print out our data.
.. code-block:: python
import serial
# Establish the connection on a specific port
ser = serial.Serial('com3', 9600)
while True:
# Read the newest output from the Arduino
dataPacket=ser.readline()
dataPacket=str(dataPacket,'utf-8')
splitPacket=dataPacket.split(" ")
q0=float(splitPacket[0])
q1=float(splitPacket[1])
print (q0,q1)
Gave an output ::
*** Remote Interpreter Reinitialized ***
353.0 382.0
367.0 391.0
370.0 391.0
365.0 390.0
371.0 391.0
Since the Arduino is sending data at about one packet every second python
has no difficulty in keeping pace. In fact there is no need to "tune" the
python script.
Vpython Graphing
----------------
.. code-block:: python
from time import time
import serial
from vpython import *
gd=graph(xmin=0,xmax=10,width=600,height=400,
title='Light Detecting Resistor',xtitle='Time',
ytitle='Arduino Output',scroll=True,fast=False)
output0=gcurve(color=color.red, label='red LED')
output1=gcurve(color=color.green, label='green LED')
# Establish the connection on a specific port
ser = serial.Serial('com3', 9600)
start = round(time())
while True:
# Read the newest output from the Arduino
dataPacket=ser.readline()
dataPacket=str(dataPacket,'utf-8')
splitPacket=dataPacket.split(" ")
q0=float(splitPacket[0])
q1=float(splitPacket[1])
now=round(time())
output0.plot(pos=(now-start,q0))
output1.plot(pos=(now-start,q1))
.. note:: from time import clock
clock() is deprecated, now use `from time import time` for Windows, Mac
and Linux machines.
Use time() to provide a time line for the plot, running in seconds. The
advantage here is that we can plot and label the curves according to the
Arduino input, so we just have to match up the sequence on the Arduino to
that of the packet used by vpython.
.. figure:: ../figures/vpython2lcdsgraph.png
:width: 662
:height: 503
:alt: vpython graph used with 2 LCDs
Light shone on LCDs, we know which plot corresponds to which led
Compare this to the output using just the serial plotter, :ref:`Serial Communication`
the auto-sizing feature of the plotter creates that jagged plot output, we
have two plots that we cannot customize, whereas vpython allows us to
show what the plots actually are. Later on we shall see how important this
can be.
Ardiotech Dial Gauges
---------------------
In some instances it would make sense to display the information as a gauge.
Making a gauge in most python guis is fairly similar, we can use the example
given by Ardiotech `Gauge using tkinter `_.
Import gaugelib and use the output from our packet to show the value
on our gauge. Astute readers will realize that we could have difficulties
because we need to read the packet with an infinite loop and at the same
time the gui requires its own infinite loop. Unless we are careful we shall
have a blocking situation. If we try to resolve this with an after() call it
remains blocked or the dial does not show. The solution given here is not
optimal, but it seems to work with this set of conditions.
.. code-block:: python
from tkinter import Tk
import serial
import gaugelib
def Packet():
# Read the newest output from the Arduino
dataPacket=ser.readline()
dataPacket=str(dataPacket,'utf-8')
splitPacket=dataPacket.split(" ")
q0=float(splitPacket[0])
q1=float(splitPacket[1])
p1.set_value(int(q0))
p2.set_value(int(q1))
# root.after(0,Packet)
root = Tk()
p1 = gaugelib.DrawGauge2(
root,
max_value=1023,
min_value=0,
size=200,
bg_col='black',
unit = "Temp. °C",bg_sel = 2)
p1.pack(side='left')
p2 = gaugelib.DrawGauge2(
root,
max_value=1023,
min_value=0,
size=200,
bg_col='black',
unit = "Humid %",bg_sel = 2)
p2.pack(side='right')
ser = serial.Serial('com3', 9600)
while 1:
Packet()
root.update() # no root.mainloop()
#root.after(0,Packet)
#root.mainloop()
The module gaugelib allows you to select the gauge type, near circular
gaugelib.DrawGauge2 or semi-circular gaugelib.DrawGauge3, dial limits, size,
background colour, the units shown on the gauge and background type.
.. topic:: Methods to unblock the GUI
When the packet is called place update() immediately after and comment out
mainloop(). You can try using after() with various times after uncommenting
mainloop() and commenting out update().
.. figure:: ../figures/ardiotech.png
:width: 447
:height: 241
:alt: ardiotech dial gauges
:align: center
If you want to experiment with different gauges, still using the LDR Arduino
script, go to :ref:`Making a Gauge in Python`.