My Pi Description

My Experiences With the Raspberry Pi -- Tracking My Learning -- My Pi Projects

Tuesday, November 12, 2013

Adding Graphical Users Interface to Graphing Temperature Measurements

I started a series of blog posts back in July about making temperature measurements with sensors using the one-wire interface to the Pi. I described the hardware, software requirements, and the python script I wrote to make and report the measurements.
Earlier this month, I added graphing capability using RRDtool and PyRRD. The python script is, of course, included. Prior to that post, I presented a tutorial on programming with RRDtool and PyRRD. The graphical results are pretty impressive. My only complaint concerns actually running the script. Before making any measurements, the user has to answer quite a few questions that appear on the terminal. Answering all of those questions, each time I run the script, got a bit tedious, so I thought about adding a graphical users interface, i.e. popping a window for those questions and answers. This post reports on those efforts.
Before proceeding any further, I hope you readers have taken a look at my post proceeding this one (Oct. 18). While developing and testing my GUI I ran into a serious problem. The script must be run from root because of access to the the GPIO hardware on the Pi. I found that Linux would not give me permission to access the graphical system running as root. Look at that post to see how that problem was handled.
When you launch the script here is the default window that pops up. I'll talk about why I mentioned default window in a little bit:
Taking this from the top of the window, I'll discuss each of the widgets, in turn: I have two temperature sensors, one on the breadboard the other on the end of a cable. With the radio buttons I can choose either sensor, or choose both sensors. You might think two check boxes would be more appropriate than the three radio buttons. But, the radio buttons work better with the program that actually makes the measurements (the program that calls for the GUI window).
Next are two entry boxes for applying a legend to the graph for each sensor. The default has only the breadboard sensor enabled because of the radio buttons selection above, which is why the cable entry box is grayed out. Select cable sensor and the entry box for cable can be edited and the breadboard entry box is grayed out. Select both sensors and neither entry box is grayed out. If you don't type anything in the entry boxes the default text (as shown) will appear on the graph. RRDtool has its own rules and some may seem strange. Here, for example, if you wish to have a colon in your legend (cable: water temperature), you must escape the colon with the backslash character (cable\: water temperature).
After the legend comes the title for the graph. A default string is included. RRDtool does not require you to escape colons in the title, but does require you to escape spaces.
Comments are optional. You must escape colons here too, just like the legends.
Next we can choose the graph background color. Black, I think, looks good on the screen, but if you wish to send the graph to your printer, it would use an awful lot of ink. Consequently, we have an option of having a graph with a white (actually light gray) background, You can choose to create both graphs if you wish. In the future, I would like add the possibility of a custom color graph. That would have new windows popping up from the basic window, something I want to try coding. Like the situation with the sensor widgets, check boxes might seem to be more efficient, bur radio buttons work better with the script making the measurements.
The width of the graph is hard-coded to 600 pixels, but the height is programmable with the slider control. You can choose values from 100 to 400 pixels in increments of 100. A shorter graph is useful when the variation in the measurement values is small (nearly a straight, horizontal line). I have seen where RRDtool actually repeats values on the Y axis if the graph height is large and there is a small variation in measurement values. RRDtool selects the values on the Y axis, not the programmer.
Next, we have the number of measurements and the interval between measurements. The smallest interval being one minute.
Finally, we come to the matter of file names. As of now (meaning I may alter this in the future), the directory name is hard-coded in the script. I have a directory for all temperature measurements, but make up a new sub-directory, under that, based on the date (for example: 2013_10_25 for Oct. 25, 2013). The file name asked for by the widget is a base name without extension. Depending on the number of sensors used, and the number of graphs generated, we will make three to five files for each run of the script. The measurements used for the graph are stored in files with an .rrd extension - one file for each sensor. There is a .png file for each graph (one for black background, one for white). Finally, there is a .txt file generated to store all of the results, along with the measurement times, in an easily read form (the .rrd files are not easily read). This .txt file is another feature that is new with this version. If, for example, the base file name is rodger and we were to use both sensors, and generate both graphs, we would generate the following five files: rodger.txt, rodger_bread.rrd, rodger_cable.rrd, rodger_black.prn, and rodger_white.prn.
The check box to the left of the file name is to protect the files from being overwritten if they already exist. It looks in the directory with the current date. Putting a check mark in the box allows the files to be overwritten.
Let's put some values into the entry boxes but we'll make errors in all of the boxes:
After pressing the "Continue" button, the red error messages show up to the right of the entry boxes. The legend and comment boxes have non-escaped colons and the title has a non-escaped space. The measurement interval can not be zero (the number of measurements will give the same error if they total to zero). The error message will appear if a non-numeric character appears in the number of measurements or any of the measurement interval boxes.
There are three possible error messages that can appear to the right of "Base Filename". They are:
  • Only numbers, letters, and underscore
  • Will overwrite existing file ("Allow File Overwrite" not checked
  • Must enter a file name (if you leave the box empty)
Once all of the corrections have been made, hitting the "Continue" button will kill the window and allow the program that makes and graph temperature measurements to proceed. If you hit the "Quit" button the window closes and the calling script terminates.
One other action happens before the window closes. All of the parameters entered in the window widgets are saved into a configuration file. The next time the program is run, all of the parameters are loaded into the window rather than the default values. This should save a lot of time if the same run is repeated or only a few changes are made. If a lot of changes are to be made a press of the "Default" button will bring up all of the default values.
Let's do an actual run. The cable sensor is placed into a 12oz. glass of hot tap water and allowed to cool to ambient temperature. Here is the window:
After pressing the "Continue" button the window closes and you see the terminal window:
The terminal window shows the parameters chosen on the GUI. It gives you a last look at your selections. If you decide you wanted something else, simply hit Ctrl C to terminate the script. A careful reader will notice the parameters on the terminal screen do not match the parameters chosen in the window above. That is because I forgot to save the screen shot, so two different runs are represented.
Below the parameters it says that the 1-wire modules had to be loaded. They are not loaded upon boot-up, so every time the Pi is powered up, or rebooted, the 1-wire modules must be loaded. The script checks to see if the modules are loaded, and, if not, loads them. Subsequent runs of the script will not need to load the modules if power stays applied and the Pi is not rebooted. Next, the script displays when the first measurements will be made. Recall that measurements are synchronized with the measurement interval. Since one minute was selected, we wait for the seconds to be 00. Next, we see the display of several measurements showing the day of the week, date and time, sensor, and the temperature as measured. The measurement results, along with time and sensor, are also displayed on my 16 character by two line Led Display.
After all of the measurements have been made, as stipulated by the value in "Number Of Measurements", the script will stop. If you wish to stop the script prematurely, you can simply issue a Ctrl C or press the switch on the breadboard.
Once the script is terminated, the terminal display looks similar to the figure above. This information is from yet another run. I did not coordinate that aspect of this post very well. You see the last of the measurements followed by a salutation and the reporting of the fact that there were no glitches.
Originally, I had problems with measurement failures where the device file could not be read. I call that a glitch and keep track of the number of glitches for the entire run. My solution to recover from these glitches is to unload and reload the 1-wire modules. For every glitch, I only unload and reload the modules a maximum of three times. If there is no recovery after the tree tries, I stop the script and report a message. This problem has not reoccurred, I have not seen any glitches for a long time.
The start time and stop time, are followed by long numbers. These numbers are the number of seconds since January 1, 1970. This is how RDTool records numbers in the .rrd files. If you want the time on the graph to be correct, you must apply these long numbers to the measurements. I report those numbers at the end here in case you wish to investigate the .rrd files. The information in the .rrd files look like gibberish if viewed with a text editor. You have to issue a RRDtool command to see the contents of these files. For more information see my post "RRDtool For Dummies Like Me" under the topic "What Measurement Values Go Into the Database".
Let's look at the code. The code is divided into two scripts, one for the GUI and the other for the main program that makes, graphs, and records the temperature measurements. The reason for a separate script for the GUI was to minimize changes to the main script. The main script is derived from the code shown in my blog entry, "Graphing Real Temperature Data Using RRDtool and PyRRD". The code for the GUI was developed independently, and has test code at the bottom so it can be run by itself, to check its operation. The GUI script was made a callable module by saving it as a .pyc file. The first 425 lines of the GUI code becomes a single function, guiwindow(). All of the parameters collected in the window are passed to the main script by line 103 of the main script:
     variable_list = guiwindow()

Code For the GUI Window:

I know it's a rather long script (another reason for making it separate from the main script), but a lot goes on here. Each element of the window (widgets) must be defined. Some of the widgets, when clicked on by the user, spurn actions. These actions must be defined. A lot of code is devoted to making the appearance correct. Placement of widgets is somewhat of a challenge. As in most GUI applications, user inputs are checked to make sure required parameters are not missed, or errors made. The operator is made aware of these errors by messages so he knows what to correct. The error checking requires a lot of code. Just look at the function proceed(). Most of the widgets have error messages included in their definations.
The development of the GUI is done using a module called Tkinter. It is probably available for all, common, Linux distributions for the Pi. I have developed all of my code using Python 2.7 so my line to import Tkinter is: "from Tkinter import *". In Python 3.x, that line would be "from tkinter import *". Tkinter is not just for Python, and not just for Linux.
So, how does one get started developing GUIs for their Raspberry Pi projects? There are many references, including "The Python Library Reference". This document points you to other sources, including the Python Wiki, a source with even more sources. One source you need to have available, constantly, is "Tkinter 8.5 Reference: a GUI for Python" from the Computer Center of New Mexico Tech. You need this for no other reason then getting the syntax correct. It is really a great asset to GUI development. Another good source of help is my code above, and code of others, for you to see practical examples. There are even YouTube videos with Tkinter tutorials. There is a ton of stuff out there on the net on Tkinter.
There are a few other items of interest in this script. Note the use of the Subprocess module in the function "getdirectory()". This is a way of issuing the Linux Ls command from Python. Here, Ls is used to see if a directory has been previously made. If it does not exist, the function makes the directory. Another cool thing is what happens, after the user presses "Continue" and the inputs have been found to contain no errors. Before the window closes and operation passed to the main program, all of the user's input gets saved to a file. When the window is opened again, the information in this file populates the widgets. This is done using a module called Pickle (actually cPickle. A version developed using C that runs faster than Pickle). Check it out, Pickle is a very efficient way to do the job and saves you from writing many lines of code.

Main Script For Making, Graphing, and Recording Temperature Measurements:

Since this code for the main script was discussed in my earlier post, I'm not going to say much about it. One element I added is saving the results to a text file. I discussed this earlier in the post. Lines 281 - 288, 312 - 319, and 327 - 334 handle this task. The directory and file name come from the GUI window.
I almost forgot, here are the two graphs: