====== How to make a thermometer ====== {{:processing:processing_thermometer.png}} The following examples will show you: * How to get data from the Internet. * How to scrape data. * How to draw fonts. * How to create a simple graphic meter. * How to invoke an action periodically. You won't find much description below. I may add some later, but my intent here is just to do a structured code dump. Because applet support has been removed in [[http://processing.org/|Processing 2]] and [[http://processingjs.org/|Processing.js]] doesn't allow downloading arbitrary files, linking to live examples that work in the browser from this page is not possible. ===== Getting files from the Internet ===== {{:processing:processing_get_internet_page.png?nolink&480|}} The built-in function ''loadStrings'' will load local files or URLs. /** * Gets a file from a URL and prints it out to the console. * * Copyright (C) 2012 Mithat Konar */ final String THE_URL = "http://www.w3.org"; String[] lines; void setup() { lines = loadStrings(THE_URL); if (lines != null) { println("there are " + lines.length + " lines"); for (int i=0; i < lines.length; i++) { println(lines[i]); } println("There were " + lines.length + " lines."); } else println("Problem loading " + THE_URL); } This version adds exception handling and graphic feedback: /** * Gets a file from a URL and prints it out to the console. * This version uses exceptions and adds graphic feedback. * * Copyright (C) 2012 Mithat Konar */ // constants final String THE_URL = "http://www.w3.org"; final int CANVAS_BORDER=10; // global variables String[] lines; void setup() { size(160, 90); smooth(); try { lines = loadStrings(THE_URL); for (int i=0; i < lines.length; i++) { println(lines[i]); } println("There were " + lines.length + " lines."); // Turn the canvas green to show that all went well. background(#00cc00); } catch (Exception e) { // In case there is an error, print out the error to the console and // turn the canvas red with a big 'X' in it. println("Problem loading " + THE_URL); println(e); // Turn the canvas red with a big 'X' in it to indicate error. background(#ff0000); strokeWeight(5); line(CANVAS_BORDER, CANVAS_BORDER, width-CANVAS_BORDER, height-CANVAS_BORDER); line(CANVAS_BORDER, height-CANVAS_BORDER, width-CANVAS_BORDER, CANVAS_BORDER); } println("Exiting app."); } ===== Scraping XML for data ===== {{:processing:processing_get_xml_page.png?nolink&480|}} Grabbing selected data from an XML source is easy with Processing's ''XML'' class. In this example, we load an RSS feed (which is XML) and extract some data from it. /** * Gets data from Yahoo RSS weather feed, picks off some information, * and prints it to the console. * * Copyright (C) 2012-2013 Mithat Konar */ // Constants: // change the URL to get the desired location and units // (see http://developer.yahoo.com/weather/). final String URL = "http://weather.yahooapis.com/forecastrss?w=12781882&u=f"; // path in XML data to current conditions: final String ELEMENT_CONDITION="channel/item/yweather:condition"; // attribute name in element above for current temperature: final String ATTRIBUTE_CONDITION_TEMPERATURE = "temp"; // path in XML data to element that holds units of measurement: final String ELEMENT_UNITS = "channel/yweather:units"; // attribute name in element above that holds units for temperature: final String ATTRIBUTE_UNIT_TEMPERATURE = "temperature"; // misc. final int CANVAS_BORDER=10; // Functions: void setup() { size(160, 90); smooth(); try { XML weather = loadXML(URL); // instantiate an XML object from URL. XML condition = weather.getChild(ELEMENT_CONDITION); // pull current cond. XML units = weather.getChild(ELEMENT_UNITS); // pull units data // Turn the canvas green to show that all went well. background(#00cc00); // "\u00b0" is the degree symbol println("The content:"); println(condition.getInt(ATTRIBUTE_CONDITION_TEMPERATURE) + "\u00b0 " + units.getString(ATTRIBUTE_UNIT_TEMPERATURE)); println("End content."); } catch (Exception e) { println("Problem loading " + URL); println(e); // Turn the canvas red with a big 'X' in it to indicate error. background(#ff0000); strokeWeight(5); line(CANVAS_BORDER, CANVAS_BORDER, width-CANVAS_BORDER, height-CANVAS_BORDER); line(CANVAS_BORDER, height-CANVAS_BORDER, width-CANVAS_BORDER, CANVAS_BORDER); } println("Exiting app."); } ===== A digital thermometer ===== {{:processing:processing_thermometer_digital.png?nolink|}} Fonts are graphics in Processing. To get the following to work, you'll need to create the *.vlw file using the //Tools > Create Font...// menu item. /* * Gets temperature data from Yahoo RSS feed and displays it numerically * on the canvas. * * Copyright (C) 2012-2013 Mithat Konar */ // Constants: // change the URL to get the desired location and units // (see http://developer.yahoo.com/weather/). final String URL = "http://weather.yahooapis.com/forecastrss?w=12781882&u=f"; // path in XML data to current conditions: final String ELEMENT_CONDITION="channel/item/yweather:condition"; // attribute name in element above for current temperature: final String ATTRIBUTE_CONDITION_TEMPERATURE = "temp"; // path in XML data to element that holds units of measurement: final String ELEMENT_UNITS = "channel/yweather:units"; // attribute name in element above that holds units for temperature: final String ATTRIBUTE_UNIT_TEMPERATURE = "temperature"; // misc. final int CANVAS_BORDER=10; // Functions: void setup() { size(250, 140); smooth(); try { XML weather = loadXML(URL); // instantiate an XML object from URL XML condition = weather.getChild(ELEMENT_CONDITION); // pull current cond. XML units = weather.getChild(ELEMENT_UNITS); // pull units data background(#296783); PFont font; // we'll use this to hold font data font = loadFont("Monospaced.bold-48.vlw"); textFont(font, 48); textAlign(CENTER, CENTER); fill(#ffffff); text(condition.getInt(ATTRIBUTE_CONDITION_TEMPERATURE) + "\u00b0" + units.getString(ATTRIBUTE_UNIT_TEMPERATURE), width/2, height/2); } catch (Exception e) { println("Problem loading " + URL); println(e); // Turn the canvas red with a big 'X' in it to indicate error. background(#ff0000); strokeWeight(5); line(CANVAS_BORDER, CANVAS_BORDER, width-CANVAS_BORDER, height-CANVAS_BORDER); line(CANVAS_BORDER, height-CANVAS_BORDER, width-CANVAS_BORDER, CANVAS_BORDER); } println("Exiting app."); } Abstracting the drawing with a function: /** * Gets temperature data from Yahoo RSS feed and displays it numerically * on the canvas. This version uses a function to abstract the drawing. * * Copyright (C) 2012-2013 Mithat Konar */ // Constants: // change the URL to get the desired location and units // (see http://developer.yahoo.com/weather/). final String URL = "http://weather.yahooapis.com/forecastrss?w=12781882&u=f"; // path in XML data to current conditions: final String ELEMENT_CONDITION="channel/item/yweather:condition"; // attribute name in element above for current temperature: final String ATTRIBUTE_CONDITION_TEMPERATURE = "temp"; // path in XML data to element that holds units of measurement: final String ELEMENT_UNITS = "channel/yweather:units"; // attribute name in element above that holds units for temperature: final String ATTRIBUTE_UNIT_TEMPERATURE = "temperature"; // misc. final int CANVAS_BORDER=10; // Functions: void setup() { size(250, 140); smooth(); try { XML weather = loadXML(URL); // instantiate an XML object from URL XML condition = weather.getChild(ELEMENT_CONDITION); // pull current cond. XML units = weather.getChild(ELEMENT_UNITS); // pull units data background(#296783); drawDigitalTemp(condition.getInt(ATTRIBUTE_CONDITION_TEMPERATURE), units.getString(ATTRIBUTE_UNIT_TEMPERATURE), width/2, height/2); } catch (Exception e) { println("Problem loading " + URL); println(e); // Turn the canvas red with a big 'X' in it to indicate error. background(#ff0000); strokeWeight(5); line(CANVAS_BORDER, CANVAS_BORDER, width-CANVAS_BORDER, height-CANVAS_BORDER); line(CANVAS_BORDER, height-CANVAS_BORDER, width-CANVAS_BORDER, CANVAS_BORDER); } println("Exiting app."); } /** * Render the temperature numerically at a given canvas location. */ void drawDigitalTemp(int temperature, String units, int x, int y) { PFont font; font = loadFont("Monospaced.bold-48.vlw"); textFont(font, 48); textAlign(CENTER, CENTER); fill(#ffffff); text(temperature + "\u00b0" + units, x, y); } ===== Adding a graphic meter ===== {{:processing:processing_thermometer_meter.png?nolink|}} Representing quantities graphically. Note also the use of Javadoc-style documentation comments for the functions. /** * Gets temperature data from Yahoo RSS feed and displays it in a meter * as well as numerically. * * Copyright (C) 2012-2013 Mithat Konar */ // Constants: // change the URL to get the desired location and units // (see http://developer.yahoo.com/weather/). final String URL = "http://weather.yahooapis.com/forecastrss?w=12781882&u=f"; // path in XML data to current conditions: final String ELEMENT_CONDITION="channel/item/yweather:condition"; // attribute name in element above for current temperature: final String ATTRIBUTE_CONDITION_TEMPERATURE = "temp"; // path in XML data to element that holds units of measurement: final String ELEMENT_UNITS = "channel/yweather:units"; // attribute name in element above that holds units for temperature: final String ATTRIBUTE_UNIT_TEMPERATURE = "temperature"; // misc. final int BORDER=10; // Functions: void setup() { size(200, 100); smooth(); try { XML weather = loadXML(URL); // instantiate an XML object from URL XML condition = weather.getChild(ELEMENT_CONDITION); // pull current cond. XML units = weather.getChild(ELEMENT_UNITS); // pull units data background(#296783); int temperature = condition.getInt(ATTRIBUTE_CONDITION_TEMPERATURE); drawDigitalTemp(temperature, units.getString(ATTRIBUTE_UNIT_TEMPERATURE), width/2, height/2); drawMeter(temperature, -40, 120, 10, height-2*BORDER, width-10-BORDER, BORDER); } catch (Exception e) { println("Problem loading " + URL); println(e); // Turn the canvas red with a big 'X' in it to indicate error. background(#ff0000); strokeWeight(5); line(BORDER, BORDER, width-BORDER, height-BORDER); line(BORDER, height-BORDER, width-BORDER, BORDER); } println("Exiting app."); } /** * Render a temperature numerically at a given canvas location. * * @param temperature the temperature to be drawn (int) * @param units the units of measure used for temperatre (String) * @param x the horizontal location of the display (int) * @param y the vertical location of the display (int) * * @return void */ void drawDigitalTemp(int temperature, String units, int x, int y) { PFont font; font = loadFont("Monospaced.bold-48.vlw"); textFont(font, 48); textAlign(CENTER, CENTER); fill(#ffffff); text(temperature + "\u00b0" + units, x, y); } /** * Render a value on a vertical meter. * * @param val the value to be drawn (float) * @param minimum val's value that corresponds to no deflection (float) * @param minimum val's value that corresponds to maximum deflection (float) * @param w the width of the meter (int) * @param h the height of the meter (int) * @param x the horizontal location of the meter (int) * @param y the vertical location of the meter (int) * * @return void */ void drawMeter(float val, float minimum, float maximum, int w, int h, int x, int y) { // calculate height of indicator in pixels float barHeight=map(val, minimum, maximum, 0, h); // draw the indicator strokeWeight(1); stroke(#ff0000); fill(#e00000); rect(x, y+(h-barHeight), w, barHeight); // draw the frame noFill(); stroke(#1F4D62); rect(x, y, w, h); } ===== Updating the data ===== Don't sleep. Poll instead. The following updates the temperature display every minute. /** * Gets temperature data from Yahoo RSS feed and displays it in a meter * as well as numerically; updates periodically. * * Copyright (C) 2012-2013 Mithat Konar */ // Constants: // change the URL to get the desired location and units // (see http://developer.yahoo.com/weather/). final String URL = "http://weather.yahooapis.com/forecastrss?w=12781882&u=f"; // path in XML data to current conditions: final String ELEMENT_CONDITION="channel/item/yweather:condition"; // attribute name in element above for current temperature: final String ATTRIBUTE_CONDITION_TEMPERATURE = "temp"; // path in XML data to element that holds units of measurement: final String ELEMENT_UNITS = "channel/yweather:units"; // attribute name in element above that holds units for temperature: final String ATTRIBUTE_UNIT_TEMPERATURE = "temperature"; // misc. final int BORDER=10; // Global variables: int oldMinute; // used to keep track of time last update was made. // Functions: void setup() { size(200, 100); smooth(); background(#296783); oldMinute = minute(); updateTemp(); } void draw() { // updateTemp every minute int currentMinute = minute(); if (oldMinute != currentMinute) { updateTemp(); oldMinute = currentMinute; } } /** * Update the display of the current temperature. * * @return void */ void updateTemp() { try { XML weather = loadXML(URL); // instantiate an XML object from URL XML condition = weather.getChild(ELEMENT_CONDITION); // pull current cond. XML units = weather.getChild(ELEMENT_UNITS); // pull units data background(#296783); int temperature = condition.getInt(ATTRIBUTE_CONDITION_TEMPERATURE); drawDigitalTemp(temperature, units.getString(ATTRIBUTE_UNIT_TEMPERATURE), width/2, height/2); drawMeter(temperature, -40, 120, 10, height-2*BORDER, width-10-BORDER, BORDER); // uncomment println below for debugging. // println("updateTemp() called at " + hour() + ":" + minute() + ":" + second() + // ". " + condition.getInt(ATTRIBUTE_CONDITION_TEMPERATURE) + " degrees."); } catch (Exception e) { println("Problem loading " + URL); println(e); // Turn the canvas red with a big 'X' in it to indicate error. background(#ff0000); strokeWeight(5); line(BORDER, BORDER, width-BORDER, height-BORDER); line(BORDER, height-BORDER, width-BORDER, BORDER); } } /** * Render the temperature numerically at a given canvas location. * * @param temperature the temperature to be drawn (int) * @param units the units of measure used for temperatre (String) * @param x the horizontal location of the display (int) * @param y the vertical location of the display (int) * * @return void */ void drawDigitalTemp(int temperature, String units, int x, int y) { PFont font; font = loadFont("Monospaced.bold-48.vlw"); textFont(font, 48); textAlign(CENTER, CENTER); fill(#ffffff); text(temperature + "\u00b0" + units, x, y); } /** * Render a value on a vertical meter. * * @param val the value to be drawn (float) * @param minimum val's value that corresponds to no deflection (float) * @param minimum val's value that corresponds to maximum deflection (float) * @param w the width of the meter (int) * @param h the height of the meter (int) * @param x the horizontal location of the meter (int) * @param y the vertical location of the meter (int) * * @return void */ void drawMeter(float val, float minimum, float maximum, int w, int h, int x, int y) { // calculate height of indicator in pixels float barHeight=map(val, minimum, maximum, 0, h); // draw the indicator strokeWeight(1); stroke(#ff0000); fill(#e00000); rect(x, y+(h-barHeight), w, barHeight); // draw the frame noFill(); stroke(#1F4D62); rect(x, y, w, h); }