<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns="http://purl.org/rss/1.0/">




    



<channel rdf:about="http://nitric.co.za/site-feed/RSS">
  <title>Nitric Software Laboratory RSS</title>
  <link>http://nitric.co.za</link>

  <description>
    
      Updates and blog posts from Nitric Software Laboratory in Cape Town
    
  </description>

  

  
            <syn:updatePeriod>daily</syn:updatePeriod>
            <syn:updateFrequency>1</syn:updateFrequency>
            <syn:updateBase>2011-07-07T11:34:51Z</syn:updateBase>
        

  <image rdf:resource="http://nitric.co.za/logo.png"/>

  <items>
    <rdf:Seq>
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/dial-224-to-have-the-title-artist-of-the-current-song-spoken-to-you"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/portfolio/esurvey"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/clementine-excellent-music-player"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/fakeexcel-class"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/we-have-moved"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/scite-how-i-configure-my-python-editor-of-choice"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/disper-for-easily-switching-displays-in-ubuntu"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/themeroller-dev-tool-bookmarklet"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/who"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/old-rotary-dialler-telephones"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/reading-the-daily-maverick-on-kindle"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/products/emon"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/portfolio/watermanlive-1"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/blog-posts/listen.py-little-script-to-stream-internet-radio-for-an-hour-and-then-stop"/>
      
      
        <rdf:li rdf:resource="http://nitric.co.za/contact"/>
      
    </rdf:Seq>
  </items>

</channel>


  <item rdf:about="http://nitric.co.za/blog-posts/dial-224-to-have-the-title-artist-of-the-current-song-spoken-to-you">
    <title>Dial 224 to have the title &amp; artist of the current song spoken  to you</title>
    <link>http://nitric.co.za/blog-posts/dial-224-to-have-the-title-artist-of-the-current-song-spoken-to-you</link>
    <description>Using espeak, python, dbus and clementine we added a feature to Alexander Bar's rotary dial phone system so that patrons can dial a number to find out the title and artist of the current playing song.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Long time no post, what with us opening <a class="external-link" href="http://alexanderbar.co.za/">Alexander Bar</a> downstairs from our offices. One of the coolest, silliest and most hilarious features of Alexander Bar is its python-and-arduino-powered rotary dial phone system that Edward will have to explain in more detail in future. Until today the system allowed people at different tables in the bar to call each other, or to call the bar for a drink which makes a soft red light flash and a ringing sound play over the speakers, or to stream the BBC World Service over the internet or to leave a comment.</p>
<p>Today we added the new feature of being able to have the title and artist of whatever song is currently playing (in <a class="external-link" href="http://code.google.com/p/clementine-player/">Clementine</a> - cannot sing its praises enough) spoken to you.</p>
<p><a class="external-link" href="http://code.google.com/p/clementine-player/wiki/MPRIS">This useful wiki page</a> showed me how easy it is to get the current playing song's metadata from Clementine via dbus in python. I first tried using <a class="external-link" href="https://launchpad.net/python-espeak">python-espeak</a> but seems like it's not yet possible or easy to get the spoken text as wav data. So I opted rather for just running through espeak command using  subprocess to convert to wav and then made that into a CherryPy web script. When someone dials 224 then Edward's phone system calls this little web script which runs on the same box and as the same user Clementine.</p>
<p>Here's my CherryPy script - other dependencies are Apache2, mod_wsgi and espeak and python-dbus which were installed already in Ubuntu 11.10</p>
<p> </p>
<pre><pre><span class="sd">''' WSGI script to return wav of spoken text that says the title and artist of the current playing song in Clementine'''</span> 
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">stderr</span>
<span class="n">currentdir</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span> <span class="n">__file__</span> <span class="p">))</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">currentdir</span><span class="p">)</span>
<span class="kn">import</span> <span class="nn">cherrypy</span>
<span class="kn">import</span> <span class="nn">dbus</span>
<span class="kn">import</span> <span class="nn">subprocess</span>
<span class="kn">import</span> <span class="nn">tempfile</span>
<span class="kn">import</span> <span class="nn">cherrypy</span>

<span class="n">env</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span>
<span class="c">#Oh dear: assumption that it's always :0</span>
<span class="n">env</span><span class="p">[</span><span class="s">'DISPLAY'</span><span class="p">]</span><span class="o">=</span><span class="s">':0'</span>

<span class="k">class</span> <span class="nc">Root</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
    
    <span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
        <span class="c"># Clementine lives on the Session bus</span>
        <span class="n">session_bus</span> <span class="o">=</span> <span class="n">dbus</span><span class="o">.</span><span class="n">SessionBus</span><span class="p">()</span>

        <span class="c"># Get Clementine's player object, and then get an interface from that object,</span>
        <span class="c"># otherwise we'd have to type out the full interface name on every method call.</span>
        <span class="n">player</span> <span class="o">=</span> <span class="n">session_bus</span><span class="o">.</span><span class="n">get_object</span><span class="p">(</span><span class="s">'org.mpris.clementine'</span><span class="p">,</span> <span class="s">'/Player'</span><span class="p">)</span>
        <span class="n">iface</span> <span class="o">=</span> <span class="n">dbus</span><span class="o">.</span><span class="n">Interface</span><span class="p">(</span><span class="n">player</span><span class="p">,</span> <span class="n">dbus_interface</span><span class="o">=</span><span class="s">'org.freedesktop.MediaPlayer'</span><span class="p">)</span>

        <span class="c"># Call a method on the interface</span>
        <span class="n">metadata</span> <span class="o">=</span> <span class="n">iface</span><span class="o">.</span><span class="n">GetMetadata</span><span class="p">()</span>
        <span class="n">text</span> <span class="o">=</span> <span class="s">'The title of the song playing now is: "</span><span class="si">%s</span><span class="s">"; by the artist "</span><span class="si">%s</span><span class="s">" '</span> <span class="o">%</span> <span class="p">(</span><span class="n">metadata</span><span class="p">[</span><span class="s">"title"</span><span class="p">],</span> <span class="n">metadata</span><span class="p">[</span><span class="s">"artist"</span><span class="p">])</span>
        
        <span class="c"># Have espeak convert that to a wav in a poncy received pronunciation male voice speaking quite slowly:</span>
        <span class="n">p</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">([</span><span class="s">'espeak'</span><span class="p">,</span> <span class="s">'-s'</span><span class="p">,</span> <span class="s">'110'</span><span class="p">,</span> <span class="s">'-ven-rp+m3'</span><span class="p">,</span> <span class="s">'--stdin'</span><span class="p">,</span> <span class="s">'--stdout'</span><span class="p">],</span> <span class="n">stdin</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">env</span><span class="o">=</span><span class="n">env</span><span class="p">)</span>
        <span class="n">wavdata</span><span class="p">,</span> <span class="n">errror</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">communicate</span><span class="p">(</span><span class="nb">input</span><span class="o">=</span><span class="n">text</span><span class="p">)</span>
        <span class="n">cherrypy</span><span class="o">.</span><span class="n">response</span><span class="o">.</span><span class="n">headers</span><span class="p">[</span><span class="s">'Content-Type'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'audio/wav'</span>
        <span class="k">return</span> <span class="n">wavdata</span>
    <span class="n">index</span><span class="o">.</span><span class="n">exposed</span><span class="o">=</span><span class="mi">1</span>


<span class="n">application</span> <span class="o">=</span> <span class="n">cherrypy</span><span class="o">.</span><span class="n">Application</span><span class="p">(</span><span class="n">Root</span><span class="p">(),</span> <span class="bp">None</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span><span class="o">==</span><span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">t</span> <span class="o">=</span> <span class="n">tempfile</span><span class="o">.</span><span class="n">NamedTemporaryFile</span><span class="p">(</span><span class="n">suffix</span><span class="o">=</span><span class="s">'.wav'</span><span class="p">,</span> <span class="n">delete</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
    <span class="n">r</span> <span class="o">=</span> <span class="n">Root</span><span class="p">()</span>
    <span class="n">wavdata</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">index</span><span class="p">()</span>
    <span class="n">t</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">wavdata</span><span class="p">)</span>
    <span class="n">name</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">name</span>
    <span class="n">t</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
    <span class="n">subprocess</span><span class="o">.</span><span class="n">call</span><span class="p">([</span><span class="s">'play'</span><span class="p">,</span> <span class="n">name</span><span class="p">])</span>
    <span class="n">os</span><span class="o">.</span><span class="n">unlink</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre>
</pre>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2012-04-19T16:34:02Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/portfolio/esurvey">
    <title>eSurvey</title>
    <link>http://nitric.co.za/portfolio/esurvey</link>
    <description>eSurvey is a unique new electronic survey platform that we developed
for our client Outprosys. Outprosys is a leading document processing
and data capture provider.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a class="external-link" href="http://www.outprosys.com/home/quote.php">eSurvey</a> is a unique new electronic survey platform that we developed for our client <a class="external-link" href="http://www.outprosys.com">Outprosys</a>. <a class="external-link" href="http://www.outprosys.com/">Outprosys</a> is a leading document processing and data capture provider.</p>
<p><a class="external-link" href="http://www.outprosys.com/home/quote.php">eSurvey</a> allows market research companies to take their existing paper surveys online - and digitally offline. First, <b>scanned images</b> of the survey are loaded into the system. Then using a drag 'n drop tool form fields are overlayed on top of the images. Quotas are then set up and assigned to different interviewers. The interviewers log in on their laptops, tablets or smartphones and all pages and questions are downloaded for offline use on the mobile browser (no app installation required). The interviewer then hits the road, finds respondents and captures their responses while offline. When online again they upload their captured data and reports are quickly generated in spreadsheeet format.  <a class="external-link" href="http://www.outprosys.com/home/quote.php"></a></p>
<p><a class="external-link" href="http://www.outprosys.com/home/quote.php">eSurvey</a> is made with a <a class="external-link" href="http://jquerymobile.com/">jQuery Mobile</a> user interface. It makes use of the html5 applicationCache and localStorage APIs to allow for offline capturing. The server side is a CherryPy python application with a MySQL database.</p>
<p>For more info and a demo see here: <a href="http://www.outprosys.com/home/quote.php" target="_blank">http://www.outprosys.com/home/quote.php</a></p>
<p><img class="image-inline" src="esurvey.png" /></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-12-07T14:03:02Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/clementine-excellent-music-player">
    <title>Clementine = excellent music player</title>
    <link>http://nitric.co.za/blog-posts/clementine-excellent-music-player</link>
    <description>Having the right music player is vitally important to being able to get dev work done.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a class="external-link" href="http://www.clementine-player.org/">Clementine</a> is great. Does playlists nicely. Does lovely crossfades that makes it feel like the machine has soul. You can first import your music for fast searching of metadata but you can also just play straight from filesystem. Has a nice artist info thing that fetches some stuff about the current song off the net. And it hasn't crashed once unlike Banshee or Rhythmbox. Nice one. We'll be using it in <a class="external-link" href="http://www.alexanderbar.co.za">the bar</a>.</p>
<p><a class="external-link" href="http://www.clementine-player.org/">http://www.clementine-player.org/</a></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-12-06T13:50:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/fakeexcel-class">
    <title>FakeExcel class</title>
    <link>http://nitric.co.za/blog-posts/fakeexcel-class</link>
    <description>To get the iMC client running on Ubuntu I wrote this FakeExcel class.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a class="external-link" href="http://incidentmc.com">iMC</a> is our product for managing and recording employee-related incidents. My father wrote the product in Python with wxWidgets so in theory getting it to run on Ubuntu should have been easy. However there were a few tricky issues because of dependencies on Excel and the COM interface that's available in win32 python that allows you to control Excel programatically.</p>
<p>In preparation for one of our new ventures (<a class="external-link" href="http://alexanderbar.co.za">Alexander Bar</a>) I needed to get iMC up and running on Ubuntu as we are going to use iMC to help manage our bar staff so what I did was to write a FakeExcel class which provides same methods but reads and writes the files as plain old CSV. If the file being read is a .xls I quickly run it through <a class="external-link" href="http://www.wagner.pp.ru/~vitus/software/catdoc/">xls2csv</a>. So unfortunately one loses the nice formatting and calculations in the report templates but for now it works as a solution to be able to get data in and out of iMC. There'll be many missing methods and properties as I only wrote replacements for what was being used in iMC.</p>
<p>By the way if you're in Gauteng go check out the iMC stand at the <a class="external-link" href="http://hr-africa.com/hrde/">HRD Expo</a> at the Sandton Convention centre this week.</p>
<pre>import os
import subprocess
import csv
import tempfile

'''
FakeExcel.py - reading/writing of "xls" files (actually csv)

If the file being read is a .xls we quickly run it through xls2csv. <br />Please use, modify, redistribute freely. Lots of missing methods and properties - please post any modifications/improvements. <br />It would be very nice if someone made this work with Python Uno so we could retain formatting &amp; calculations.

Dependency: catdoc package (apt-get install catdoc)

Nicholas Spagnoletti
nicholas@nitric.co.za
2011-10-02

'''

def openx(path):
    'Open a file on windows using whatever system default opener is and if that fails then with gnome-open'
    try:
        os.startfile(path)
    except AttributeError:
        subprocess.Popen(["gnome-open", path])



class Value(object):
    'Class for getting and setting cell Values'
    def __init__(self, parent, rownum, colnum):
        self.parent=parent
        self.rownum=rownum
        self.colnum=colnum
    def __repr__(self):
        return self.Value
    def __setattr__(self, a, v):
        if a=='Value':
            self.parent._cells[(self.rownum, self.colnum)] = v
        else:
            return super(Value,self).__setattr__(a,v)
            
    def __getattr__(self, a):
        if a=='Value':
            try:
                return self.parent._cells[(self.rownum, self.colnum)]
            except KeyError:
                return None

    
class Sheet(object):
    'Class corresponding to sheet in Workbook (only ever have one sheet as we are actually dealing with csv'
    def __init__(self, parent):
        self._cells = {}
        self.parent=parent
        sample = self.parent.file.read(2096)
        dialect = csv.Sniffer().sniff(sample)
        self.parent.file.seek(0)
        r = csv.reader(self.parent.file, dialect=dialect)
        for rownum, line in enumerate(r):
            for colnum, v in enumerate(line):
                if v!= u'\x0c':
                    nv = Value(self, rownum+1, colnum+1)
                    nv.Value = v
      
            
    def Cells(self, row, col):
        nv = Value(self, row, col)
        return nv
            

class Workbooks(object):
    'Opens and parses .csv and .xls files, sets a Sheet, and provides method for saving out as .csv'
    path=None
    def __init__(self, parent):
        self.parent=parent
    def Open(self, path):
        self.path=path
        if path.endswith('.xls'):
            s = subprocess.Popen(["xls2csv", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            csv, err = s.communicate()
            t = tempfile.TemporaryFile()
            t.write(csv)
            t.seek(0)
            self.file = t
        else:
            self.file = open(path)
        
        sheet = Sheet(self)
        self.parent.ActiveSheet=sheet
        Sheets = (sheet,)
        return self
    def SaveAs(self, path):
        self.path=path
        with open(path, 'w') as f:
            w = csv.writer(f)
            keys = sorted(self.parent.ActiveSheet._cells.keys())
            cells = self.parent.ActiveSheet._cells
            prevrow = None
            for k in keys:
                row, col = k
                if prevrow!=row and row&gt;1:
                    w.writerow(line)
                if prevrow!=row:
                    line = []
                line.append(cells.get(k))
                prevrow = row
            
    def Close(self, SaveChanges=False):
        if SaveChanges:
            self.SaveAs(self.path)
        self.parent.ActiveSheet._cells = {}

class FakeExcel(object):
    'Instantiates a Workbooks object'
    def __init__(self):
        self.Workbooks = Workbooks(self)
    def Quit(self):
        pass
  
        
        

if __name__=="__main__":
    excel = FakeExcel()
    excel.Workbooks.Open('/tmp/public.xls')
    sheet = excel.ActiveSheet
    row = 1
    while sheet.Cells(row,1).Value != None:
        print sheet.Cells(row,1).Value
        row += 1
    #now let's change a val
    sheet.Cells(3,1).Value = 'Changed'
    row = 1
    while sheet.Cells(row,1).Value != None:
        print sheet.Cells(row,1).Value
        row += 1
    excel.Workbooks.SaveAs('/tmp/public.csv')
    #Open on the desktop
    openx('/tmp/public.csv')
    

</pre>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-10-02T13:49:12Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/we-have-moved">
    <title>We have moved!</title>
    <link>http://nitric.co.za/blog-posts/we-have-moved</link>
    <description>After many years of the Bandwidth Barn, we have moved to 78 Strand Street.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Yes, after many many years of being in the Bandwidth Barn, we have finally decided to move into our own offices. Our new address is 78 Strand Street. Our postal address stays the same as Box 7207, Rogge Bay, 8012.</p>
<p>We will miss many of our barn neighbours, and especially the barn staff who have always been so good to us.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Edward van Kuik</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-09-21T13:34:01Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/scite-how-i-configure-my-python-editor-of-choice">
    <title>SciTE - how I configure my python editor of choice</title>
    <link>http://nitric.co.za/blog-posts/scite-how-i-configure-my-python-editor-of-choice</link>
    <description>SciTE is what I want from an editor - fast, highly configurable and not Eclipse.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>It took me a long time to find the right editor. After trying out many IDEs and editors and always running back to <a class="external-link" href="http://www.nedit.org/">NEdit</a> for - seriously - a decade I finally switched to <a class="external-link" href="http://www.scintilla.org/SciTE.html">SciTE</a>. SciTE is what I want from an editor - fast, highly configurable and not Eclipse.</p>
<p>The first thing is to generate a python text file for calltips and autocompletion using <a class="external-link" href="http://www.koders.com/python/fid7000B9C96CF2C6FB5BCE9DF700365C5B2A1F36A7.aspx">this script</a>. (I had to add some exception catching in places but unfortunately have lost my modified version of that file, sorry.)</p>
<p>Then although it's very old you can also grab the PHP one <a class="external-link" href="http://www.scintilla.org/php.api">from here</a>. (This doesn't bother me as my PHP is also very old)</p>
<p>Then from the SciTE Options menu choose 'Open User Options File'  - here is the config that I put there with all the options I've decided are best for me:</p>
<pre>#python autocomplete and tooltips settings - python.api is in my home dir<br />api.*.py=$(SciteUserHome)/python.api<br />autocomplete.python.ignorecase=1<br />autocomplete.python.start.characters=.<br />autocomplete.python.fillups=(<br />calltip.python.ignorecase=1<br />calltip.python.word.characters=._$(chars.alpha)$(chars.numeric)<br /><br />#php autocomplete and tooltip settings - php.api is in my home dir<br />api.$(file.patterns.php)=$(SciteUserHome)/php.api<br />autocomplete.php.ignorecase=1<br />autocomplete.php.start.characters=.<br />autocomplete.php.fillups=(<br />calltip.php.ignorecase=1<br />calltip.php.word.characters=._$(chars.alpha)$(chars.numeric)<br /><br />#also use words in current buffer as choices for autocomplete<br />autocompleteword.automatic=1<br />#even if only one option then must still explicitly choose<br />autocomplete.choose.single=0<br /><br />#utf8, of course, silly<br />code.page=65001<br />#check whether file modified by another process<br />load.on.activate=1<br />#ask me whether to save unsaved files when closing<br />are.you.sure=1<br />#use same instance of scite if already running<br />check.if.already.open=1<br />#allow open this number of files at same time<br />buffers=25<br />#keep list of recent files<br />save.recent=1<br />#when starting scite reopen previous buffers<br />save.session=1<br />#show full path in title bar to current file<br />title.full.path=1<br />#show line numbers<br />line.margin.visible=1<br />#width of line numbers<br />line.margin.width=4<br /><br />#indentation<br />tabsize=4<br />indent.size=4<br />#ooh, tabs please<br />use.tabs=1<br />#wrap long lines<br />wrap=1<br /><br />#let's always use monospace<br />font.base=$(font.monospace)<br />font.comment=$(font.monospace)<br />font.text=$(font.monospace)</pre>
<p>For more options and documentation on these look here:<br /><a class="external-link" href="http://www.scintilla.org/SciTEDoc.html">http://www.scintilla.org/SciTEDoc.html</a></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-08-25T09:30:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/disper-for-easily-switching-displays-in-ubuntu">
    <title>disper - for easily switching displays &amp; resolutions on the fly in Ubuntu</title>
    <link>http://nitric.co.za/blog-posts/disper-for-easily-switching-displays-in-ubuntu</link>
    <description>Thanks to disper I can easily switch between primary and secondary monitors or changing resolutions without restarting X even if using the Nvidia drivers.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Our home machine is our media watching box with an Nvidia GeForce 9500 GT with Nvidia driver version 270.41.06. We have an RGB-over-cat5 cable (another whole story which Edward will have to write about) running to the TV which acts as the second monitor in a TwinView configuration. The resolutions of the TV and primary monitor are slightly different which wasn't really a problem in Ubuntu Classic but since Unity there appeared a second menu bar on the primary monitor because of the overlap (Having the monitors in a left-and-right layout didn't work in practice either because when full screening Flash video it would full screen on the wrong monitor)</p>
<p>Since I don't really need to ever use both monitors at the same time I was looking for an easy way to switch between using either the primary monitor and the TV. Generally, when finding something to watch, one browses around for a bit and then buffer up the stream, full screen it and then go and sit down to watch. Opening nvidia-settings and going through a bunch of steps every time wasn't a good enough solution and having to restart X session would of course be a total no-no.</p>
<p>Then I found <a class="external-link" href="http://willem.engen.nl/projects/disper/">disper</a> which is fantastic and works perfectly for me. It is a command line tool to change your display settings on the fly. I set this command to run when pressing ctrl+scrolllock:</p>
<p><code>disper --cycle-stages="-S -r 1360x768: -s" --cycle</code></p>
<p>(which means that each time that command is run disper will cycle between showing the secondary and primary monitors. I had to specify the resolution for the secondary monitor.)</p>
<p>Voila. Can sit back and enjoy the <a class="external-link" href="http://www.thedailyshow.com">Daily Show</a>. Disper will also prove useful at talks and presentations which are always preceded by ritualistic fighting with the projector.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-08-08T09:16:20Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/themeroller-dev-tool-bookmarklet">
    <title>Themeroller Dev Tool Bookmarklet</title>
    <link>http://nitric.co.za/blog-posts/themeroller-dev-tool-bookmarklet</link>
    <description>For the last few months the very useful Themeroller Dev Tool Bookmarklet has been broken in newish versions of Firefox. I've posted a fixed version.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>We love <a class="external-link" href="http://jqueryui.com">jQuery UI</a> and <a class="external-link" href="http://jqueryui.com/themeroller/">Themeroller</a> which allows us - and our clients - to re-theme their web apps interactively. Then a few months ago after an upgrade to Firefox 4 we found the Themeroller bookmarklet no longer worked. Someone posted a solution on the jQuery UI forum which while not rocket science is a little bit of a hassle to do so I thought I'd Be Nice and put my fixed one up for others to use:</p>
<p><a class="external-link" href="http://themeroller.nitric.co.za/">http://themeroller.nitric.co.za/</a></p>
<p>Open that page and drag the button onto your Firefox bookmarks toolbar. Then open on any 'themerollered' page and voila - works perfectly for me in Firefox 5.0</p>
<p>Here's that forum posting by the way:<br /><a class="external-link" href="http://forum.jquery.com/topic/the-firefox-themeroller-bookmarklet-does-not-update-the-html-page-when-changing-theme-colors">http://forum.jquery.com/topic/the-firefox-themeroller-bookmarklet-does-not-update-the-html-page-when-changing-theme-colors</a></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-07-22T14:15:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/who">
    <title>Who</title>
    <link>http://nitric.co.za/who</link>
    <description>About the people at Nitric.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2>Edward van Kuik</h2>
<p>Edward van Kuik completed his Bachelor of  Science majoring in Computer Science from University of Cape Town, South  Africa in 1996. After university he worked at the Energy and  Development Research Centre designing applications to optimise  engineering design problems using genetic algorithms.  In 1998, Edward  started working for De Beers Marine as a Software Engineer in the  Mineral Resources Unit working on mostly seismic-related systems in C++.  In 2001, he started his own Company, Nitric Industries, (the one you're  reading about now) and has been looking a little stressed ever since.  Edward van Kuik believes in Linux, open standards and open source technologies.</p>
<h2>Nicholas Spagnoletti</h2>
<p>Nicholas is an experienced software  developer. He graduated from UCT with an humanities degree in 2000.  While studying he worked for his father's sofware business and began his  PHP career by doing the company website. He gained more experience as a  web developer at the .com era sports website Sportal before joining  Edward full-time at Nitric in 2001. Since then he has worked on hundreds  of projects of all sizes. The bulk of work has been in designing and  implementing business operations systems for other small-to-medium  enterprises. He is also a playwright - look out for his play <a href="http://londonroad.co.za/">London Road</a></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-07-21T11:05:00Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/old-rotary-dialler-telephones">
    <title>Old Rotary Dialler telephones</title>
    <link>http://nitric.co.za/blog-posts/old-rotary-dialler-telephones</link>
    <description>Making a little 10-telephone exchange using a 10 channel sound card and some Arduino's.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I wanted to make a little 10-telephone exchange out of old rotary dial telephones. I've been collecting old rotary dial phones from markets and antique shops. Nicholas even bought an old rotary dial payphone! So I bought a <a class="external-link" href="http://www.m-audio.com/products/en_us/Delta1010LT.html">Delta-1010LT</a> sound card because someone said it will work nicely with ALSA. I found a nice python jack library simply called 'jack' to chat to a jackd sound server via numpy arrays. I decided to just use the <a class="external-link" href="http://arduino.cc/en/Main/ArduinoBoardUno">Arduino</a> running <a class="external-link" href="http://firmata.org/wiki/Main_Page">firmata</a> and use <a class="external-link" href="https://bitbucket.org/tino/pyfirmata/src">pyfirmata</a> to read to the various pins on the Arduino to detect the onhook and <a class="external-link" href="http://en.wikipedia.org/wiki/Pulse_dialing">pulse dialling</a> and also to blink a little LED on the phone when the phone rings (using the ringers would be too loud for my purpose). Then I hugely abused some ethernet cables way beyond their original purpose. (I have a habit of doing that.)</p>
<p>You can dial one of the other phones, and even break-in to another conversation for a multi-way chat. Oh and you can dial 222 and get the BBC World Service via gstreamer. :)</p>
<p>Thanks to Colin Braye for helping me rewire and soldier up some phones and circuits like the 8 op-amps I suddenly realised I needed.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Edward van Kuik</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-07-21T10:45:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/reading-the-daily-maverick-on-kindle">
    <title>Reading The Daily Maverick on Kindle</title>
    <link>http://nitric.co.za/blog-posts/reading-the-daily-maverick-on-kindle</link>
    <description>I love the Daily Maverick but frustratingly they don't offer a Kindle edition - Calibre to the rescue!</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a class="external-link" href="http://www.thedailymaverick.co.za">The Daily Maverick</a> is great. Excellent journalism and intelligent opinion. Unfortunately they're completely seduced by shiny baubles and are pouring all their resources into creating an iPad version set to launch next month.</p>
<p>They are quite stubborn about producing a Kindle version; doubly frustrating because I'd actually happily <i>pay</i> for this (I already subscribe to the <a class="external-link" href="http://www.mg.co.za">M&amp;G</a> kindle edition).</p>
<p><a class="external-link" href="http://calibre-ebook.com">Calibre</a> has a very useful feature which can turn an RSS feed (or a number of RSS feeds) into an ebook for reading later on your Kindle or similar device.</p>
<p>Enter the Daily Maverick's RSS feed: http://www.thedailymaverick.co.za/rss as a Custom news source.</p>
<p>Select 'MOBI' as the output format and then transfer to Kindle either with the USB cable or by sending it to your Kindle email address (remember if you send it to your free Kindle address it will be delivered free of charge as long as you're on a WIFI network) The result is a near perfect reading experience similar to what you get if you subscribe to the <a class="external-link" href="http://www.mg.co.za/">M&amp;G</a> or Newsweek in the Kindle store.</p>
<p>.</p>
<p style="text-align: center; "><img alt="Calibre" class="image-inline" src="../images/calibretdm.png/image_large" /></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-07-20T11:02:49Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/products/emon">
    <title>Emon</title>
    <link>http://nitric.co.za/products/emon</link>
    <description>Emon is a monitoring tool that replaces paper-based logging with an SMS based capturing system. Data capturers out in the field send codes via SMS to the Emon number - 31028. Emon validates and stores the information and provides web and email reports and exports of the data.
</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a class="external-link" href="http://emon.co.za">Emon</a> stands for Environmental Monitoring and can be used for any kind of manual data capture.<br />Data is captured by codes that are predefined for each <a class="external-link" href="http://emon.co.za/">Emon</a> service. This allows many records to be batched together in a single text message.</p>
<p style="text-align: center; "><img alt="emon" class="image-inline" src="../images/emon.jpg" /></p>
<p><a class="external-link" href="http://emon.co.za/">Emon</a> is currently in use at a number of landfill sites where it is  necessary to know the quantity and type of waste. As vehicles arrive at  each site, monitoring staff compose an SMS with single letter codes of  what kind of vehicle and what kind of waste is arriving. Once they reach  the 160 character limit of a text message, they send off the SMS to  31028 and start with a new message. Thus hundreds of records can be  captured in almost real time at a cost of less than R10 using any type  of cellphone.</p>
<p> </p>
<p style="text-align: center; "><img alt="Example of a report showing data collected by Emon" class="image-inline" src="../images/Screenshot.png/image_preview" /></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-07-20T10:25:28Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/portfolio/watermanlive-1">
    <title>Watermanlive</title>
    <link>http://nitric.co.za/portfolio/watermanlive-1</link>
    <description>We developed Watermanlive for our client Waterman Marine Consultancy who are based in the French riviera. Their business is in management and surveying services for motor and sailing yachts.

</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a class="external-link" href="http://watermanlive.com">Watermanlive</a> (or WML for short) is a tool for managing new builds, major refits, vessel maintenance and compliance to international maritime regulations and standards. <a class="external-link" href="http://watermanlive.com/">WML</a> is for captains, insurance brokers, underwriters, surveyors, engineers, operators and owners to manage risk, build, maintenance and compliance issues on board their vessels.<br /><br /><a class="external-link" href="http://watermanlive.com">WML</a> has a powerful and flexible surveying module. Choose from preset survey templates or create your own set of survey questions. Once you've set up the survey you can load it on a mobile device such as a smartphone, Android tablet or iPad which you can take on board the vessel with you to conduct the survey. When you're online again, upload the survey results and <a class="external-link" href="http://watermanlive.com/">WML</a> will automatically create issues and notifications on all flagged items.<br /><br />Built in to <a class="external-link" href="http://watermanlive.com/">WML</a> is an issue tracking system for managing issues, damage claims and warranty.  These can be manually created at any time. Also when a survey is conducted, an issue is automatically created for any item that requires attention - this could be a component needing technical attention, a missing or expired certificate or document or any other compliance or safety issue.<br /><br /><a class="external-link" href="http://watermanlive.com/">WML</a> also keeps track of the vessel's documents  - photos, scans of certificates, regulations, contracts with crew, financial information etc. These are presented with an easily navigable gallery-style web interface and can be easily managed online.<br /><br /><br /></p>
<p style="text-align: center; "><img alt="Showing dashboard of vessels " class="image-inline" src="../images/wmlscreenshot.png" /></p>
<p style="text-align: left; "><a class="external-link" href="http://watermanlive.com/">WML</a><i> was developed using Python, CherryPy, MySQL, Mako templates, jQuery and jQuery UI.</i></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-07-20T09:55:00Z</dc:date>
    <dc:type>Page</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/blog-posts/listen.py-little-script-to-stream-internet-radio-for-an-hour-and-then-stop">
    <title>listen.py: Little script to stream internet radio for an hour and then stop </title>
    <link>http://nitric.co.za/blog-posts/listen.py-little-script-to-stream-internet-radio-for-an-hour-and-then-stop</link>
    <description>To avoid accidentally wasting lots of bandwidth I made this little python script that will play an internet stream using gstreamer for an hour and then stop. </description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<div id="content-core">
<div class="kssattr-macro-text-field-view kssattr-templateId-newsitem_view kssattr-atfieldname-text plain" id="parent-fieldname-text">
<p>In the mornings I like listening to the BBC World Service  while having breakfast and getting ready. I used to use my Worldspace  satellite receiver but a while ago they ceased operating and a few  months ago they stopped broadcasting the BBC. I realised I would have to  listen to it over the net. The Worldspace radio had a very useful  builtin FM modulator so you could listen around your home on normal FM  radios. I purchased a cheap FM modulator to attach to my computer so  that we can listen all over the flat on normal FM radios.</p>
<p>To solve the problem of accidentally leaving the stream running all  day and chowing all our expensive international bandwidth, I wrote this  little app which uses wx to make a very simple GUI that has a button to  start playing the stream (using gstreamer of course) and a timer that  counts down. After an hour the stream stops.</p>
<p>Dependencies are wxpython and the python-gstreamer bindings. On Ubuntu you can install these packages:</p>
<pre>apt-get install python-gst0.10 python-wxgtk2.8</pre>
<p>Here's the script, where you can choose between the World Service and  BBC Radio 4 - add any other streams to the streams list of tuples.</p>
<pre>#!/usr/bin/python<br />import gst<br />import wx<br />import time<br /><br />player = gst.element_factory_make("playbin", "player")<br /><br />streams = [<br /> ('BBC World Service', 'http://mp32.bbc.streamuk.com:80/'),<br /> ('BBC Radio 4', 'mms://wm-live.bbc.net.uk/wms/bbc_ami/radio4/radio4_bb_live_int_ep1_sl0?BBC-UID=b4adbcdf5ba7eea337440a77610973cee5bd1676f0b0524162a83074f35cc068&amp;amp;SSO2-UID=')<br />]<br /><br /><br />class mainwin(wx.Frame):<br />    playfor=60*60<br />    s=playfor<br />    <br />    def __init__(self, parent=None):<br />        super(mainwin,self).__init__(parent, wx.ID_ANY, title='Listen to the BBC')<br />        <br />        panel = wx.Panel(self, wx.ID_ANY)<br />        sizer = wx.BoxSizer(wx.VERTICAL)<br />        self.button = wx.Button(panel, wx.ID_ANY, 'Start')<br />        self.cb = wx.ComboBox(panel, choices=[s[0] for s in streams],style=wx.CB_DROPDOWN|wx.CB_READONLY,value=streams[0][0])<br />        <br />        self.timer = wx.Timer(self)<br />        self.Bind(wx.EVT_TIMER, self.update, self.timer)<br />        <br />        self.label = wx.StaticText(panel)<br />            <br />        panel.SetSizer(sizer)<br />        sizer.Add(self.cb)<br />        sizer.Add(self.button)<br />        sizer.Add(self.label)<br />        <br />        <br />        self.Bind(wx.EVT_BUTTON, self.toggle, self.button)<br />        <br />    <br />    def toggle(self, evt=None):        <br />        label = self.button.GetLabel()<br />        station = self.cb.GetValue()<br />        if label=='Start':<br />            for s, uri in streams:<br />                if station==s:<br />                    player.set_property('uri', uri)<br />            player.set_state(gst.STATE_PLAYING)<br />            self.button.SetLabel('Stop')<br />            self.timer.Start(1000)<br />            <br />        elif label=='Stop':<br />            self.s=1<br />            self.update()<br />            player.set_state(gst.STATE_NULL)<br />            self.button.SetLabel('Start')<br />    <br />    def update(self, evt=None):<br />        self.s -= 1<br />        if self.s &lt;= 0:<br />            player.set_state(gst.STATE_NULL)<br />            <br />            self.button.SetLabel('Start')<br />            self.timer.Stop()<br />            self.s=self.playfor<br />            self.label.SetLabel('')<br />        else:<br />            mins = self.s / 60<br />            secs = self.s % 60<br />            self.label.SetLabel("%02d:%02d" % (mins,secs))<br />        <br />        <br /><br />if __name__ == '__main__':<br />    app = wx.PySimpleApp()<br />    frame = mainwin().Show()<br />    app.MainLoop()</pre>
</div>
</div>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>Nicholas Spagnoletti</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-06-25T16:38:44Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://nitric.co.za/contact">
    <title>Contact</title>
    <link>http://nitric.co.za/contact</link>
    <description>Our Contact Details</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a accesskey="9" class="link-overlay" href="contact-info" rel="#pb_2" title="Contact">Click here to contact us via web form</a></p>
<h2>Postal Address</h2>
<p>Box 7207<br />Roggebaai<br />8012</p>
<h2>Physical Address</h2>
<p>78 Strand Street<br /> Cape Town<br />8001</p>
<h2>Phone Numbers</h2>
<p><b>Phone: </b>021-300-1073 (VOIP landline hence the funny prefix)<br /><b>Fax: </b>086 710 8657</p>
<h2>Email Addresses</h2>
<p>Accounts Enquiries: accounts@nitric.co.za<br />General Enquiries: info@nitric.co.za</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>admin</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-06-13T20:55:00Z</dc:date>
    <dc:type>Page</dc:type>
  </item>





</rdf:RDF>

