From c6fbe1a6f87f9eccb43ac7e9c95e85f18ddd6177 Mon Sep 17 00:00:00 2001 From: Pieter Date: Thu, 7 Apr 2016 14:35:36 +0200 Subject: [PATCH 1/8] added keithley driver --- docs/examples/Keithley_example.ipynb | 470 ++++++++++++++++++ .../instrument_drivers/tektronix/Keithley.py | 338 +++++++++++++ 2 files changed, 808 insertions(+) create mode 100644 docs/examples/Keithley_example.ipynb create mode 100644 qcodes/instrument_drivers/tektronix/Keithley.py diff --git a/docs/examples/Keithley_example.ipynb b/docs/examples/Keithley_example.ipynb new file mode 100644 index 000000000000..3848a6854e6f --- /dev/null +++ b/docs/examples/Keithley_example.ipynb @@ -0,0 +1,470 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example script for Keithley driver" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/*\r\n", + " * Qcodes Jupyter/IPython widgets\r\n", + " */\r\n", + "require([\r\n", + " 'nbextensions/widgets/widgets/js/widget',\r\n", + " 'nbextensions/widgets/widgets/js/manager'\r\n", + "], function (widget, manager) {\r\n", + "\r\n", + " var UpdateView = widget.DOMWidgetView.extend({\r\n", + " render: function() {\r\n", + " window.MYWIDGET = this;\r\n", + " this._interval = 0;\r\n", + " this.update();\r\n", + " },\r\n", + " update: function() {\r\n", + " this.display(this.model.get('_message'));\r\n", + " this.setInterval();\r\n", + " },\r\n", + " display: function(message) {\r\n", + " /*\r\n", + " * display method: override this for custom display logic\r\n", + " */\r\n", + " this.el.innerHTML = message;\r\n", + " },\r\n", + " remove: function() {\r\n", + " clearInterval(this._updater);\r\n", + " },\r\n", + " setInterval: function(newInterval) {\r\n", + " var me = this;\r\n", + " if(newInterval===undefined) newInterval = me.model.get('interval');\r\n", + " if(newInterval===me._interval) return;\r\n", + "\r\n", + " me._interval = newInterval;\r\n", + "\r\n", + " if(me._updater) clearInterval(me._updater);\r\n", + "\r\n", + " if(me._interval) {\r\n", + " me._updater = setInterval(function() {\r\n", + " me.send({myupdate: true});\r\n", + " if(!me.model.comm_live) {\r\n", + " console.log('missing comm, canceling widget updates', me);\r\n", + " clearInterval(me._updater);\r\n", + " }\r\n", + " }, me._interval * 1000);\r\n", + " }\r\n", + " }\r\n", + " });\r\n", + " manager.WidgetManager.register_widget_view('UpdateView', UpdateView);\r\n", + "\r\n", + " var HiddenUpdateView = UpdateView.extend({\r\n", + " display: function(message) {\r\n", + " this.$el.hide();\r\n", + " }\r\n", + " });\r\n", + " manager.WidgetManager.register_widget_view('HiddenUpdateView', HiddenUpdateView);\r\n", + "\r\n", + " var SubprocessView = UpdateView.extend({\r\n", + " render: function() {\r\n", + " var me = window.SPVIEW = this;\r\n", + " me._interval = 0;\r\n", + " me._minimize = '';\r\n", + " me._restore = '';\r\n", + "\r\n", + " // in case there is already an outputView present,\r\n", + " // like from before restarting the kernel\r\n", + " $('.qcodes-output-view').not(me.$el).remove();\r\n", + "\r\n", + " me.$el\r\n", + " .addClass('qcodes-output-view')\r\n", + " .attr('qcodes-state', 'docked')\r\n", + " .html(\r\n", + " '
' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '
' +\r\n", + " '
'\r\n",
+       "                );\r\n",
+       "\r\n",
+       "            me.clearButton = me.$el.find('.qcodes-clear-output');\r\n",
+       "            me.minButton = me.$el.find('.qcodes-minimize');\r\n",
+       "            me.outputArea = me.$el.find('pre');\r\n",
+       "            me.subprocessList = me.$el.find('span');\r\n",
+       "            me.abortButton = me.$el.find('.qcodes-abort-loop');\r\n",
+       "\r\n",
+       "            me.clearButton.click(function() {\r\n",
+       "                me.outputArea.html('');\r\n",
+       "                me.clearButton.addClass('disabled');\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.abortButton.click(function() {\r\n",
+       "                me.send({abort: true});\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.$el.find('.js-state').click(function() {\r\n",
+       "                var oldState = me.$el.attr('qcodes-state'),\r\n",
+       "                    state = this.className.substr(this.className.indexOf('qcodes'))\r\n",
+       "                        .split('-')[1].split(' ')[0];\r\n",
+       "\r\n",
+       "                // not sure why I can't pop it out of the widgetarea in render, but it seems that\r\n",
+       "                // some other bit of code resets the parent after render if I do it there.\r\n",
+       "                // To be safe, just do it on every state click.\r\n",
+       "                me.$el.appendTo('body');\r\n",
+       "\r\n",
+       "                if(oldState === 'floated') {\r\n",
+       "                    me.$el.draggable('destroy').css({left:'', top: ''});\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                me.$el.attr('qcodes-state', state);\r\n",
+       "\r\n",
+       "                if(state === 'floated') {\r\n",
+       "                    me.$el.draggable().css({\r\n",
+       "                        left: window.innerWidth - me.$el.width() - 15,\r\n",
+       "                        top: window.innerHeight - me.$el.height() - 10\r\n",
+       "                    });\r\n",
+       "                }\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            $(window).resize(function() {\r\n",
+       "                if(me.$el.attr('qcodes-state') === 'floated') {\r\n",
+       "                    var position = me.$el.position(),\r\n",
+       "                        minVis = 20,\r\n",
+       "                        maxLeft = window.innerWidth - minVis,\r\n",
+       "                        maxTop = window.innerHeight - minVis;\r\n",
+       "\r\n",
+       "                    if(position.left > maxLeft) me.$el.css('left', maxLeft);\r\n",
+       "                    if(position.top > maxTop) me.$el.css('top', maxTop);\r\n",
+       "                }\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.update();\r\n",
+       "        },\r\n",
+       "\r\n",
+       "        display: function(message) {\r\n",
+       "            if(message) {\r\n",
+       "                var initialScroll = this.outputArea.scrollTop();\r\n",
+       "                this.outputArea.scrollTop(this.outputArea.prop('scrollHeight'));\r\n",
+       "                var scrollBottom = this.outputArea.scrollTop();\r\n",
+       "\r\n",
+       "                if(this.$el.attr('qcodes-state') === 'minimized') {\r\n",
+       "                    this.$el.find('.qcodes-docked').click();\r\n",
+       "                    // always scroll to the bottom if we're restoring\r\n",
+       "                    // because of a new message\r\n",
+       "                    initialScroll = scrollBottom;\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                this.outputArea.append(message);\r\n",
+       "                this.clearButton.removeClass('disabled');\r\n",
+       "\r\n",
+       "                // if we were scrolled to the bottom initially, make sure\r\n",
+       "                // we stay that way.\r\n",
+       "                this.outputArea.scrollTop(initialScroll === scrollBottom ?\r\n",
+       "                    this.outputArea.prop('scrollHeight') : initialScroll);\r\n",
+       "            }\r\n",
+       "\r\n",
+       "            var processes = this.model.get('_processes') || 'No subprocesses';\r\n",
+       "            this.abortButton.toggleClass('disabled', processes.indexOf('Measurement')===-1);\r\n",
+       "            this.subprocessList.text(processes);\r\n",
+       "        }\r\n",
+       "    });\r\n",
+       "    manager.WidgetManager.register_widget_view('SubprocessView', SubprocessView);\r\n",
+       "});\r\n"
+      ],
+      "text/plain": [
+       ""
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/html": [
+       ""
+      ],
+      "text/plain": [
+       ""
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "import matplotlib.pyplot as plt\n",
+    "import time\n",
+    "import numpy as np\n",
+    "from imp import reload\n",
+    "\n",
+    "import qcodes as qc\n",
+    "\n",
+    "qc.set_mp_method('spawn')  # force Windows behavior on mac\n",
+    "\n",
+    "# this makes a widget in the corner of the window to show and control\n",
+    "# subprocesses and any output they would print to the terminal\n",
+    "qc.show_subprocess_widget()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Keithley driver\n",
+    "\n",
+    "What to do:\n",
+    "    \n",
+    "* implement add functionality of the driver\n",
+    "* add documentation to the functions\n",
+    "    "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['IntsValidator',\n",
+       " 'Keithley_2700',\n",
+       " 'NumbersValidator',\n",
+       " 'StringValidator',\n",
+       " 'VisaInstrument',\n",
+       " '__builtins__',\n",
+       " '__cached__',\n",
+       " '__doc__',\n",
+       " '__file__',\n",
+       " '__loader__',\n",
+       " '__name__',\n",
+       " '__package__',\n",
+       " '__spec__',\n",
+       " 'bool_to_str',\n",
+       " 'dummy',\n",
+       " 'logging',\n",
+       " 'pi',\n",
+       " 'time',\n",
+       " 'validators']"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import qcodes.instrument_drivers\n",
+    "import qcodes.instrument_drivers.tektronix.Keithley as keith; reload(keith)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Connected to:  KEITHLEY INSTRUMENTS INC., MODEL 2700, 0792116, B06  /A02    in 0.21s\n"
+     ]
+    }
+   ],
+   "source": [
+    "k1 = keith.Keithley_2700('Keithley', 'GPIB1::15::INSTR')\n",
+    "k1.add_parameter('READ', get_cmd='READ?', label='Keithley value', get_parser=float)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "current mode: VOLT:DC\n"
+     ]
+    }
+   ],
+   "source": [
+    "print('current mode: %s' % k1.get('mode'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "collapsed": false,
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "measure: -0.762987\n",
+      "measure: -0.777320\n",
+      "measure: -0.783848\n",
+      "measure: -0.790359\n",
+      "measure: -0.796843\n",
+      "measure: -0.803321\n"
+     ]
+    }
+   ],
+   "source": [
+    "for i in range(6):\n",
+    "    print('measure: %.6f'  % k1.readnext() )"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[]"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "station = qc.Station('Keithley')\n",
+    "\n",
+    "# could measure any number of things by adding arguments to this\n",
+    "# function call, but here we're just measuring one, the meter mode\n",
+    "station.set_measurement(k1.mode)\n",
+    "\n",
+    "qc.active_children()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['VOLT:DC']"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "station.measure()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.5.1"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/qcodes/instrument_drivers/tektronix/Keithley.py b/qcodes/instrument_drivers/tektronix/Keithley.py
new file mode 100644
index 000000000000..4bedf38d00b0
--- /dev/null
+++ b/qcodes/instrument_drivers/tektronix/Keithley.py
@@ -0,0 +1,338 @@
+# Keithley_2700.py driver for Keithley 2700 DMM
+#
+# Pieter Eendebak , 2016 (adapt to Qcodes framework)
+# Pieter de Groot , 2008
+# Martijn Schaafsma , 2008
+# Reinier Heeres , 2008
+#
+# Update december 2009:
+# Michiel Jol 
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import time
+from numpy import pi
+from qcodes.instrument.visa import VisaInstrument
+#from qcodes.utils import validators
+from qcodes.utils.validators import Strings as StringValidator
+from qcodes.utils.validators import Ints as IntsValidator
+from qcodes.utils.validators import Numbers as NumbersValidator
+
+import logging
+
+#%% Helper functions
+
+def bool_to_str(val):
+    '''
+    Function to convert boolean to 'ON' or 'OFF'
+    '''
+    if val == True:
+        return "ON"
+    else:
+        return "OFF"
+
+
+
+#%% Driver for Keithley_2700       
+
+        
+class Keithley_2700(VisaInstrument):
+    '''
+    This is the qcodes driver for the Keithley_2700 Multimeter
+
+    Usage: Initialize with
+     =  = Keithley_2700(, address='', reset=,
+            change_display=, change_autozero=)
+
+    Status: beta-version.
+    
+    This driver will most likely work for multiple Keithley SourceMeters.
+
+    This driver does not contain all commands available, but only the ones most commonly used.
+    '''
+    def __init__(self, name, address, reset=False):
+        t0 = time.time()
+        super().__init__(name, address)
+
+        self._modes = ['']
+        self.add_parameter('IDN', get_cmd='*IDN?')
+        
+        self._modes = ['VOLT:AC', 'VOLT:DC', 'CURR:AC', 'CURR:DC', 'RES',
+            'FRES', 'TEMP', 'FREQ']
+        #self._change_display = change_display
+        #self._change_autozero = change_autozero
+        self._averaging_types = ['MOV','REP']
+        self._trigger_sent = False
+        
+        # Add parameters to wrapper
+        self.add_parameter('mode', get_cmd=':CONF?', get_parser=lambda x: x.strip().strip('"'), set_cmd=':CONF:{}', vals=StringValidator() )
+
+        self.add_parameter('trigger_count', get_cmd=self._mode_par('INIT', 'CONT'), get_parser=int, set_cmd=self._mode_par_value('INIT','CONT','{}'), vals=IntsValidator(), units='#' )
+        self.add_parameter('trigger_delay', get_cmd=self._mode_par('TRIG', 'DEL'), get_parser=float, set_cmd=self._mode_par_value('TRIG', 'DEL', '{}'), vals=NumbersValidator(min_value=0, max_value=999999.999), units='s')
+
+        self.add_parameter('trigger_continuous', get_cmd=self._mode_par('INIT', 'CONT'), get_parser=bool, set_cmd=self._mode_par_value('INIT', 'CONT', '{}'), set_parser=bool_to_str )
+        
+        self.add_parameter('averaging', get_cmd=lambda: self._current_mode_get( 'AVER:STAT'), get_parser=bool, set_cmd=lambda val: self._current_mode_set('AVER:STAT', val), set_parser=bool_to_str )
+
+        self.add_parameter('digits', get_cmd=lambda: self._current_mode_get('DIG'), get_parser=int, set_cmd=lambda val: self._current_mode_set('DIG', val)  )
+        
+        self.add_parameter('nplc', get_cmd=lambda: self._current_mode_get('NPLC'), get_parser=int, set_cmd=lambda val: self._current_mode_set('NPLC', val), units='APER'  )
+        self.add_parameter('range', get_cmd=lambda: self._current_mode_get('RANG'), get_parser=int, set_cmd=lambda val: self._current_mode_set('RANG', val), units='RANG'  )
+
+        
+        '''
+        self.add_parameter('range',
+            flags=Instrument.FLAG_GETSET,
+            units='', minval=0.1, maxval=1000, type=float)
+        self.add_parameter('trigger_source',
+            flags=Instrument.FLAG_GETSET,
+            units='')
+        self.add_parameter('trigger_timer',
+            flags=Instrument.FLAG_GETSET,
+            units='s', minval=0.001, maxval=99999.999, type=float)
+        self.add_parameter('digits',
+            flags=Instrument.FLAG_GETSET,
+            units='#', minval=4, maxval=7, type=int)
+        self.add_parameter('readval', flags=Instrument.FLAG_GET,
+            units='AU',
+            type=float,
+            tags=['measure'])
+        self.add_parameter('readlastval', flags=Instrument.FLAG_GET,
+            units='AU',
+            type=float,
+            tags=['measure'])
+        self.add_parameter('readnextval', flags=Instrument.FLAG_GET,
+            units='AU',
+            type=float,
+            tags=['measure'])
+        self.add_parameter('integrationtime',
+            flags=Instrument.FLAG_GETSET,
+            units='s', type=float, minval=2e-4, maxval=1)
+        self.add_parameter('nplc',
+            flags=Instrument.FLAG_GETSET,
+            units='#', type=float, minval=0.01, maxval=50)
+        self.add_parameter('display', flags=Instrument.FLAG_GETSET,
+            type=bool)
+        self.add_parameter('autozero', flags=Instrument.FLAG_GETSET,
+            type=bool)
+        self.add_parameter('averaging_window',
+            flags=Instrument.FLAG_GETSET,
+            units='%', type=float, minval=0, maxval=10)
+        self.add_parameter('averaging_count',
+            flags=Instrument.FLAG_GETSET,
+            units='#', type=int, minval=1, maxval=100)
+        self.add_parameter('averaging_type',
+            flags=Instrument.FLAG_GETSET,
+            type=bytes, units='')
+        self.add_parameter('autorange',
+            flags=Instrument.FLAG_GETSET,
+            units='',
+            type=bool)
+'''
+            
+
+        # add functions
+        self.add_function('readnext', units='AU', call_cmd=':DATA:FRESH?', return_parser=float)
+
+        if reset:
+            self.reset()
+        else:
+            self.get_all()
+            self.set_defaults()
+
+        t1 = time.time()
+        print('Connected to: ',
+              self.get('IDN').replace(',', ', ').replace('\n', ' '),
+              'in %.2fs' % (t1-t0))
+
+
+    def get_all(self):
+        '''
+        Reads all relevant parameters from instrument
+
+        Input:
+            None
+
+        Output:
+            None
+        '''
+        logging.info('Get all relevant data from device')
+        
+        for p in ['mode', 'trigger_count', 'trigger_continuous', 'averaging', 'digits', 'nplc']:
+            logging.debug('get %s' % p)
+            par=getattr(self,p)
+            par.get()
+            
+#        self.get_range()
+ #       self.get_trigger_delay()
+  #      self.get_trigger_source()
+   #     self.get_trigger_timer()
+    #    self.get_integrationtime()
+      #  self.get_display()
+       # self.get_autozero()
+        #self.get_averaging_window()
+        #self.get_averaging_count()
+        #self.get_averaging_type()
+        #self.get_autorange()
+                
+    def _current_mode_get(self, par, mode=None):
+        cmd=self._mode_par(mode, par)
+        return self.ask(cmd)
+
+    def _current_mode_set(self, par, val, mode=None):
+        cmd=self._mode_par_value(mode, par, val)
+        return self.write(cmd)
+
+
+# --------------------------------------
+#           functions
+# --------------------------------------
+
+    def set_mode_volt_dc(self):
+        '''
+        Set mode to DC Voltage
+
+        Input:
+            None
+
+        Output:
+            None
+        '''
+        logging.debug('Set mode to DC Voltage')
+        self.mode.set('VOLT:DC')
+        
+    def set_defaults(self):
+        '''
+        Set to driver defaults:
+        Output=data only
+        Mode=Volt:DC
+        Digits=7
+        Trigger=Continous
+        Range=10 V
+        NPLC=1
+        Averaging=off
+        '''
+
+        self.write('SYST:PRES')
+        self.write(':FORM:ELEM READ')
+            # Sets the format to only the read out, all options are:
+            # READing = DMM reading, UNITs = Units,
+            # TSTamp = Timestamp, RNUMber = Reading number,
+            # CHANnel = Channel number, LIMits = Limits reading
+
+        self.set_mode_volt_dc()
+        self.digits.set(7)
+        self.trigger_continuous.set(True)
+        self.range.set(10)
+        self.nplc.set(1)
+        self.averaging.set(False)
+        return
+        
+    def _determine_mode(self, mode):
+        '''
+        Return the mode string to use.
+        If mode is None it will return the currently selected mode.
+        '''
+        logging.debug('Determine mode with mode=%s' % mode)
+        if mode is None:
+            mode = self.mode.get_latest() # _mode(query=False)
+        if mode not in self._modes and mode not in ('INIT', 'TRIG', 'SYST', 'DISP'):
+            logging.warning('Invalid mode %s, assuming current' % mode)
+            mode = self.mode.get_latest()
+        return mode
+        
+    def set_mode(self, mode):
+        '''
+        Set the mode to the specified value
+
+        Input:
+            mode (string) : mode to be set. Choose from self._modes
+
+        Output:
+            None
+        '''
+
+        logging.debug('Set mode to %s', mode)
+        if mode in self._modes:
+            string = ':CONF:%s' % mode
+            self._visainstrument.write(string)
+
+            if mode.startswith('VOLT'):
+                self._change_units('V')
+            elif mode.startswith('CURR'):
+                self._change_units('A')
+            elif mode.startswith('RES'):
+                self._change_units('Ohm')
+            elif mode.startswith('FREQ'):
+                self._change_units('Hz')
+
+        else:
+            logging.error('invalid mode %s' % mode)
+
+        self.get_all()
+            # Get all values again because some parameters depend on mode
+
+    def _mode_par_value(self, mode, par, val):
+        '''
+        For internal use only!!
+        Create command string based on current mode
+        
+        Input:
+            mode (string) : The mode to use
+            par (string)  : Parameter
+            val (depends) : Value
+
+        Output:
+            None
+        '''
+        mode = self._determine_mode(mode)
+        string = ':%s:%s %s' % (mode, par, val)
+        return string
+    def _mode_par(self, mode, par):
+        '''
+        For internal use only!!
+        Create command string based on current mode
+        
+        Input:
+            mode (string) : The mode to use
+            par (string)  : Parameter
+            val (depends) : Value
+
+        Output:
+            None
+        '''
+        mode = self._determine_mode(mode)
+        string = ':%s:%s?' % (mode, par, )
+        return string
+        
+    def reset(self):
+        '''
+        Resets instrument to default values
+
+        Input:
+            None
+
+        Output:
+            None
+        '''
+        logging.debug('Resetting instrument')
+        self._visainstrument.write('*RST')
+        self.get_all()
+
+    # def on(self):
+    #     self.set('status', 'on')
+
+    # def off(self):
+    #     self.set('status', 'off')

From 0207d295b3a24c936f312126fe44458f3c6a99df Mon Sep 17 00:00:00 2001
From: Pieter 
Date: Fri, 8 Apr 2016 09:34:59 +0200
Subject: [PATCH 2/8] added docstrings to parameters

---
 docs/examples/Keithley_example.ipynb          | 305 ++----------------
 qcodes/instrument/function.py                 |   5 +-
 qcodes/instrument/parameter.py                |   6 +-
 .../instrument_drivers/tektronix/Keithley.py  |   3 +-
 4 files changed, 45 insertions(+), 274 deletions(-)

diff --git a/docs/examples/Keithley_example.ipynb b/docs/examples/Keithley_example.ipynb
index 3848a6854e6f..e36dbec5286e 100644
--- a/docs/examples/Keithley_example.ipynb
+++ b/docs/examples/Keithley_example.ipynb
@@ -9,249 +9,11 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 15,
    "metadata": {
     "collapsed": false
    },
-   "outputs": [
-    {
-     "data": {
-      "application/javascript": [
-       "/*\r\n",
-       " * Qcodes Jupyter/IPython widgets\r\n",
-       " */\r\n",
-       "require([\r\n",
-       "    'nbextensions/widgets/widgets/js/widget',\r\n",
-       "    'nbextensions/widgets/widgets/js/manager'\r\n",
-       "], function (widget, manager) {\r\n",
-       "\r\n",
-       "    var UpdateView = widget.DOMWidgetView.extend({\r\n",
-       "        render: function() {\r\n",
-       "            window.MYWIDGET = this;\r\n",
-       "            this._interval = 0;\r\n",
-       "            this.update();\r\n",
-       "        },\r\n",
-       "        update: function() {\r\n",
-       "            this.display(this.model.get('_message'));\r\n",
-       "            this.setInterval();\r\n",
-       "        },\r\n",
-       "        display: function(message) {\r\n",
-       "            /*\r\n",
-       "             * display method: override this for custom display logic\r\n",
-       "             */\r\n",
-       "            this.el.innerHTML = message;\r\n",
-       "        },\r\n",
-       "        remove: function() {\r\n",
-       "            clearInterval(this._updater);\r\n",
-       "        },\r\n",
-       "        setInterval: function(newInterval) {\r\n",
-       "            var me = this;\r\n",
-       "            if(newInterval===undefined) newInterval = me.model.get('interval');\r\n",
-       "            if(newInterval===me._interval) return;\r\n",
-       "\r\n",
-       "            me._interval = newInterval;\r\n",
-       "\r\n",
-       "            if(me._updater) clearInterval(me._updater);\r\n",
-       "\r\n",
-       "            if(me._interval) {\r\n",
-       "                me._updater = setInterval(function() {\r\n",
-       "                    me.send({myupdate: true});\r\n",
-       "                    if(!me.model.comm_live) {\r\n",
-       "                        console.log('missing comm, canceling widget updates', me);\r\n",
-       "                        clearInterval(me._updater);\r\n",
-       "                    }\r\n",
-       "                }, me._interval * 1000);\r\n",
-       "            }\r\n",
-       "        }\r\n",
-       "    });\r\n",
-       "    manager.WidgetManager.register_widget_view('UpdateView', UpdateView);\r\n",
-       "\r\n",
-       "    var HiddenUpdateView = UpdateView.extend({\r\n",
-       "        display: function(message) {\r\n",
-       "            this.$el.hide();\r\n",
-       "        }\r\n",
-       "    });\r\n",
-       "    manager.WidgetManager.register_widget_view('HiddenUpdateView', HiddenUpdateView);\r\n",
-       "\r\n",
-       "    var SubprocessView = UpdateView.extend({\r\n",
-       "        render: function() {\r\n",
-       "            var me = window.SPVIEW = this;\r\n",
-       "            me._interval = 0;\r\n",
-       "            me._minimize = '';\r\n",
-       "            me._restore = '';\r\n",
-       "\r\n",
-       "            // in case there is already an outputView present,\r\n",
-       "            // like from before restarting the kernel\r\n",
-       "            $('.qcodes-output-view').not(me.$el).remove();\r\n",
-       "\r\n",
-       "            me.$el\r\n",
-       "                .addClass('qcodes-output-view')\r\n",
-       "                .attr('qcodes-state', 'docked')\r\n",
-       "                .html(\r\n",
-       "                    '
' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '
' +\r\n", - " '
'\r\n",
-       "                );\r\n",
-       "\r\n",
-       "            me.clearButton = me.$el.find('.qcodes-clear-output');\r\n",
-       "            me.minButton = me.$el.find('.qcodes-minimize');\r\n",
-       "            me.outputArea = me.$el.find('pre');\r\n",
-       "            me.subprocessList = me.$el.find('span');\r\n",
-       "            me.abortButton = me.$el.find('.qcodes-abort-loop');\r\n",
-       "\r\n",
-       "            me.clearButton.click(function() {\r\n",
-       "                me.outputArea.html('');\r\n",
-       "                me.clearButton.addClass('disabled');\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            me.abortButton.click(function() {\r\n",
-       "                me.send({abort: true});\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            me.$el.find('.js-state').click(function() {\r\n",
-       "                var oldState = me.$el.attr('qcodes-state'),\r\n",
-       "                    state = this.className.substr(this.className.indexOf('qcodes'))\r\n",
-       "                        .split('-')[1].split(' ')[0];\r\n",
-       "\r\n",
-       "                // not sure why I can't pop it out of the widgetarea in render, but it seems that\r\n",
-       "                // some other bit of code resets the parent after render if I do it there.\r\n",
-       "                // To be safe, just do it on every state click.\r\n",
-       "                me.$el.appendTo('body');\r\n",
-       "\r\n",
-       "                if(oldState === 'floated') {\r\n",
-       "                    me.$el.draggable('destroy').css({left:'', top: ''});\r\n",
-       "                }\r\n",
-       "\r\n",
-       "                me.$el.attr('qcodes-state', state);\r\n",
-       "\r\n",
-       "                if(state === 'floated') {\r\n",
-       "                    me.$el.draggable().css({\r\n",
-       "                        left: window.innerWidth - me.$el.width() - 15,\r\n",
-       "                        top: window.innerHeight - me.$el.height() - 10\r\n",
-       "                    });\r\n",
-       "                }\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            $(window).resize(function() {\r\n",
-       "                if(me.$el.attr('qcodes-state') === 'floated') {\r\n",
-       "                    var position = me.$el.position(),\r\n",
-       "                        minVis = 20,\r\n",
-       "                        maxLeft = window.innerWidth - minVis,\r\n",
-       "                        maxTop = window.innerHeight - minVis;\r\n",
-       "\r\n",
-       "                    if(position.left > maxLeft) me.$el.css('left', maxLeft);\r\n",
-       "                    if(position.top > maxTop) me.$el.css('top', maxTop);\r\n",
-       "                }\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            me.update();\r\n",
-       "        },\r\n",
-       "\r\n",
-       "        display: function(message) {\r\n",
-       "            if(message) {\r\n",
-       "                var initialScroll = this.outputArea.scrollTop();\r\n",
-       "                this.outputArea.scrollTop(this.outputArea.prop('scrollHeight'));\r\n",
-       "                var scrollBottom = this.outputArea.scrollTop();\r\n",
-       "\r\n",
-       "                if(this.$el.attr('qcodes-state') === 'minimized') {\r\n",
-       "                    this.$el.find('.qcodes-docked').click();\r\n",
-       "                    // always scroll to the bottom if we're restoring\r\n",
-       "                    // because of a new message\r\n",
-       "                    initialScroll = scrollBottom;\r\n",
-       "                }\r\n",
-       "\r\n",
-       "                this.outputArea.append(message);\r\n",
-       "                this.clearButton.removeClass('disabled');\r\n",
-       "\r\n",
-       "                // if we were scrolled to the bottom initially, make sure\r\n",
-       "                // we stay that way.\r\n",
-       "                this.outputArea.scrollTop(initialScroll === scrollBottom ?\r\n",
-       "                    this.outputArea.prop('scrollHeight') : initialScroll);\r\n",
-       "            }\r\n",
-       "\r\n",
-       "            var processes = this.model.get('_processes') || 'No subprocesses';\r\n",
-       "            this.abortButton.toggleClass('disabled', processes.indexOf('Measurement')===-1);\r\n",
-       "            this.subprocessList.text(processes);\r\n",
-       "        }\r\n",
-       "    });\r\n",
-       "    manager.WidgetManager.register_widget_view('SubprocessView', SubprocessView);\r\n",
-       "});\r\n"
-      ],
-      "text/plain": [
-       ""
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "text/html": [
-       ""
-      ],
-      "text/plain": [
-       ""
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
+   "outputs": [],
    "source": [
     "import matplotlib.pyplot as plt\n",
     "import time\n",
@@ -282,7 +44,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 16,
    "metadata": {
     "collapsed": false
    },
@@ -290,28 +52,10 @@
     {
      "data": {
       "text/plain": [
-       "['IntsValidator',\n",
-       " 'Keithley_2700',\n",
-       " 'NumbersValidator',\n",
-       " 'StringValidator',\n",
-       " 'VisaInstrument',\n",
-       " '__builtins__',\n",
-       " '__cached__',\n",
-       " '__doc__',\n",
-       " '__file__',\n",
-       " '__loader__',\n",
-       " '__name__',\n",
-       " '__package__',\n",
-       " '__spec__',\n",
-       " 'bool_to_str',\n",
-       " 'dummy',\n",
-       " 'logging',\n",
-       " 'pi',\n",
-       " 'time',\n",
-       " 'validators']"
+       ""
       ]
      },
-     "execution_count": 7,
+     "execution_count": 16,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -323,7 +67,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 17,
    "metadata": {
     "collapsed": false
    },
@@ -332,7 +76,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Connected to:  KEITHLEY INSTRUMENTS INC., MODEL 2700, 0792116, B06  /A02    in 0.21s\n"
+      "Connected to:  KEITHLEY INSTRUMENTS INC., MODEL 2700, 0792116, B06  /A02    in 0.14s\n"
      ]
     }
    ],
@@ -343,7 +87,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 18,
    "metadata": {
     "collapsed": false
    },
@@ -362,7 +106,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 19,
    "metadata": {
     "collapsed": false,
     "scrolled": true
@@ -372,12 +116,12 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "measure: -0.762987\n",
-      "measure: -0.777320\n",
-      "measure: -0.783848\n",
-      "measure: -0.790359\n",
-      "measure: -0.796843\n",
-      "measure: -0.803321\n"
+      "measure: -1.044276\n",
+      "measure: -1.050299\n",
+      "measure: -1.056594\n",
+      "measure: -1.069865\n",
+      "measure: -1.075954\n",
+      "measure: -1.081997\n"
      ]
     }
    ],
@@ -436,6 +180,25 @@
     "station.measure()"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "sds\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(k1.nplc.__doc__)"
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/qcodes/instrument/function.py b/qcodes/instrument/function.py
index 0ef731224729..3094f3dd477f 100644
--- a/qcodes/instrument/function.py
+++ b/qcodes/instrument/function.py
@@ -50,12 +50,15 @@ class Function(Metadatable):
     def __init__(self, name, instrument=None,
                  call_cmd=None, async_call_cmd=None,
                  args=[], arg_parser=None, return_parser=None,
-                 **kwargs):
+                 docstring=None, **kwargs):
         super().__init__(**kwargs)
 
         self._instrument = instrument
         self.name = name
 
+        if docstring is not None:
+            self.__doc__=docstring
+            
         self._set_args(args)
         self._set_call(call_cmd, async_call_cmd,
                        arg_parser, return_parser)
diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py
index a06b37eb58df..fa0ae832e326 100644
--- a/qcodes/instrument/parameter.py
+++ b/qcodes/instrument/parameter.py
@@ -134,7 +134,7 @@ def __init__(self,
                  units=None,
                  size=None, sizes=None,
                  setpoints=None, setpoint_names=None, setpoint_labels=None,
-                 vals=None, **kwargs):
+                 vals=None, docstring=None, **kwargs):
         super().__init__(**kwargs)
 
         if names is not None:
@@ -167,6 +167,10 @@ def __init__(self,
             self.setpoint_names = setpoint_names
             self.setpoint_labels = setpoint_labels
 
+        if docstring is not None:
+            #logging.debug('add docstring to Parameter!')
+            self.__doc__ = docstring
+            
         # record of latest value and when it was set or measured
         # what exactly this means is different for different subclasses
         # but they all use the same attributes so snapshot is consistent.
diff --git a/qcodes/instrument_drivers/tektronix/Keithley.py b/qcodes/instrument_drivers/tektronix/Keithley.py
index 4bedf38d00b0..6ccbe4c4d44c 100644
--- a/qcodes/instrument_drivers/tektronix/Keithley.py
+++ b/qcodes/instrument_drivers/tektronix/Keithley.py
@@ -88,7 +88,8 @@ def __init__(self, name, address, reset=False):
 
         self.add_parameter('digits', get_cmd=lambda: self._current_mode_get('DIG'), get_parser=int, set_cmd=lambda val: self._current_mode_set('DIG', val)  )
         
-        self.add_parameter('nplc', get_cmd=lambda: self._current_mode_get('NPLC'), get_parser=int, set_cmd=lambda val: self._current_mode_set('NPLC', val), units='APER'  )
+        self.add_parameter('nplc', get_cmd=lambda: self._current_mode_get('NPLC'), get_parser=int, set_cmd=lambda val: self._current_mode_set('NPLC', val), units='APER', docstring='Get integration time in Number of PowerLine Cycles. \
+            To get the integrationtime in seconds, use get_integrationtime().'  )
         self.add_parameter('range', get_cmd=lambda: self._current_mode_get('RANG'), get_parser=int, set_cmd=lambda val: self._current_mode_set('RANG', val), units='RANG'  )
 
         

From d3335270b4decbc2bf18797752e2cbcb2e9d768c Mon Sep 17 00:00:00 2001
From: Pieter 
Date: Fri, 8 Apr 2016 17:50:17 +0200
Subject: [PATCH 3/8] improve Keithley driver with comments from PR #69

---
 qcodes/instrument/function.py                 |  2 +
 qcodes/instrument/parameter.py                |  4 +
 .../instrument_drivers/tektronix/Keithley.py  | 73 +++++++++++++------
 3 files changed, 56 insertions(+), 23 deletions(-)

diff --git a/qcodes/instrument/function.py b/qcodes/instrument/function.py
index 3094f3dd477f..ca1b1fa67c77 100644
--- a/qcodes/instrument/function.py
+++ b/qcodes/instrument/function.py
@@ -46,6 +46,8 @@ class Function(Metadatable):
         If None (default), will not wait for or read any response
     NOTE: parsers only apply if call_cmd is a string. The function forms
         of call_cmd and async_call_cmd should do their own parsing.
+    docstring: documentation string for the __doc__ field of the object
+        The __doc__ field of the instance is used by some help systems, but not all 
     '''
     def __init__(self, name, instrument=None,
                  call_cmd=None, async_call_cmd=None,
diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py
index fa0ae832e326..da76b5ebfd45 100644
--- a/qcodes/instrument/parameter.py
+++ b/qcodes/instrument/parameter.py
@@ -273,6 +273,8 @@ class StandardParameter(Parameter):
     sweep_delay: time (in seconds) to wait after each sweep step
     max_val_age: max time (in seconds) to trust a saved value from
         this parameter as the starting point of a sweep
+    docstring: documentation string for the __doc__ field of the object
+        The __doc__ field of the instance is used by some help systems, but not all 
     '''
     def __init__(self, name, instrument=None,
                  get_cmd=None, async_get_cmd=None, get_parser=None,
@@ -293,6 +295,8 @@ def __init__(self, name, instrument=None,
                 self._set_mapping = val_mapping
                 set_parser = self._set_mapping.__getitem__
 
+        if get_parser is not None and not isinstance(get_cmd, str):
+            logging.warning('get_parser is set, but will not be used')
         super().__init__(name=name, vals=vals, **kwargs)
 
         self._instrument = instrument
diff --git a/qcodes/instrument_drivers/tektronix/Keithley.py b/qcodes/instrument_drivers/tektronix/Keithley.py
index 6ccbe4c4d44c..47c3345650ed 100644
--- a/qcodes/instrument_drivers/tektronix/Keithley.py
+++ b/qcodes/instrument_drivers/tektronix/Keithley.py
@@ -47,7 +47,21 @@ def bool_to_str(val):
 
 #%% Driver for Keithley_2700       
 
+from functools import partial
         
+def parseint(v):
+    logging.debug('parseint: %s -> %d' % (v, int(v)))
+    return int(v)
+
+def parsebool(v):
+    r=bool(int(v))
+    logging.debug('parsetobool: %s -> %d' % (v, r))
+    return r
+
+def parsestr(v):
+    return v.strip().strip('"')
+            
+            
 class Keithley_2700(VisaInstrument):
     '''
     This is the qcodes driver for the Keithley_2700 Multimeter
@@ -77,22 +91,38 @@ def __init__(self, name, address, reset=False):
         self._trigger_sent = False
         
         # Add parameters to wrapper
-        self.add_parameter('mode', get_cmd=':CONF?', get_parser=lambda x: x.strip().strip('"'), set_cmd=':CONF:{}', vals=StringValidator() )
+        self.add_parameter('mode', get_cmd=':CONF?', get_parser=parsestr, set_cmd=':CONF:{}', vals=StringValidator() )
 
-        self.add_parameter('trigger_count', get_cmd=self._mode_par('INIT', 'CONT'), get_parser=int, set_cmd=self._mode_par_value('INIT','CONT','{}'), vals=IntsValidator(), units='#' )
-        self.add_parameter('trigger_delay', get_cmd=self._mode_par('TRIG', 'DEL'), get_parser=float, set_cmd=self._mode_par_value('TRIG', 'DEL', '{}'), vals=NumbersValidator(min_value=0, max_value=999999.999), units='s')
+        self.add_parameter('trigger_count', get_cmd=self._mode_par('INIT', 'CONT'),
+                           get_parser=int, set_cmd=self._mode_par_value('INIT','CONT','{}'),
+                                vals=IntsValidator(), units='#' )
+        self.add_parameter('trigger_delay', get_cmd=self._mode_par('TRIG', 'DEL'),
+                   get_parser=float, set_cmd=self._mode_par_value('TRIG', 'DEL', '{}'),
+                      vals=NumbersValidator(min_value=0, max_value=999999.999), units='s')
 
-        self.add_parameter('trigger_continuous', get_cmd=self._mode_par('INIT', 'CONT'), get_parser=bool, set_cmd=self._mode_par_value('INIT', 'CONT', '{}'), set_parser=bool_to_str )
+        self.add_parameter('trigger_continuous', get_cmd=self._mode_par('INIT', 'CONT'),
+                   get_parser=parsebool, set_cmd=self._mode_par_value('INIT', 'CONT', '{}'), set_parser=bool_to_str )
         
-        self.add_parameter('averaging', get_cmd=lambda: self._current_mode_get( 'AVER:STAT'), get_parser=bool, set_cmd=lambda val: self._current_mode_set('AVER:STAT', val), set_parser=bool_to_str )
+        self.add_parameter('averaging', get_cmd=partial(self._current_mode_get, 'AVER:STAT', parser=parsebool),
+                   set_cmd=partial(self._current_mode_set, par='AVER:STAT'),
+                   set_parser=bool_to_str )
 
-        self.add_parameter('digits', get_cmd=lambda: self._current_mode_get('DIG'), get_parser=int, set_cmd=lambda val: self._current_mode_set('DIG', val)  )
+        self.add_parameter('digits', get_cmd=partial(self._current_mode_get, 'DIG', parser=int),
+                   set_cmd=partial(self._current_mode_set, par='DIG')  )
         
-        self.add_parameter('nplc', get_cmd=lambda: self._current_mode_get('NPLC'), get_parser=int, set_cmd=lambda val: self._current_mode_set('NPLC', val), units='APER', docstring='Get integration time in Number of PowerLine Cycles. \
+        self.add_parameter('nplc', get_cmd=partial( self._current_mode_get, 'NPLC', parser=float),
+                       set_cmd=partial(self._current_mode_set, par='NPLC', mode=None), units='APER',
+                     docstring='Get integration time in Number of PowerLine Cycles. \
             To get the integrationtime in seconds, use get_integrationtime().'  )
-        self.add_parameter('range', get_cmd=lambda: self._current_mode_get('RANG'), get_parser=int, set_cmd=lambda val: self._current_mode_set('RANG', val), units='RANG'  )
 
-        
+        self.add_parameter('range', get_cmd=partial(self._current_mode_get, 'RANG',parser=float),
+                   set_cmd=partial(self._current_mode_set, par='RANG'), units='RANG'  )
+
+        self.add_parameter('integrationtime', get_cmd=partial(self._current_mode_get, 'APER', parser=float),
+                set_cmd=partial(self._current_mode_set, par='APER', mode=None), units='s',
+                vals=NumbersValidator(min_value=2e-4, max_value=1.),
+                docstring='Get integration time in seconds. To get the integrationtime as a Number of PowerLine Cycles, use get_nplc().' )
+ 
         '''
         self.add_parameter('range',
             flags=Instrument.FLAG_GETSET,
@@ -103,9 +133,6 @@ def __init__(self, name, address, reset=False):
         self.add_parameter('trigger_timer',
             flags=Instrument.FLAG_GETSET,
             units='s', minval=0.001, maxval=99999.999, type=float)
-        self.add_parameter('digits',
-            flags=Instrument.FLAG_GETSET,
-            units='#', minval=4, maxval=7, type=int)
         self.add_parameter('readval', flags=Instrument.FLAG_GET,
             units='AU',
             type=float,
@@ -118,9 +145,6 @@ def __init__(self, name, address, reset=False):
             units='AU',
             type=float,
             tags=['measure'])
-        self.add_parameter('integrationtime',
-            flags=Instrument.FLAG_GETSET,
-            units='s', type=float, minval=2e-4, maxval=1)
         self.add_parameter('nplc',
             flags=Instrument.FLAG_GETSET,
             units='#', type=float, minval=0.01, maxval=50)
@@ -141,7 +165,7 @@ def __init__(self, name, address, reset=False):
             flags=Instrument.FLAG_GETSET,
             units='',
             type=bool)
-'''
+        '''
             
 
         # add functions
@@ -171,7 +195,7 @@ def get_all(self):
         '''
         logging.info('Get all relevant data from device')
         
-        for p in ['mode', 'trigger_count', 'trigger_continuous', 'averaging', 'digits', 'nplc']:
+        for p in ['mode', 'trigger_count', 'trigger_continuous', 'averaging', 'digits', 'nplc', 'integrationtime']:
             logging.debug('get %s' % p)
             par=getattr(self,p)
             par.get()
@@ -180,7 +204,6 @@ def get_all(self):
  #       self.get_trigger_delay()
   #      self.get_trigger_source()
    #     self.get_trigger_timer()
-    #    self.get_integrationtime()
       #  self.get_display()
        # self.get_autozero()
         #self.get_averaging_window()
@@ -188,12 +211,15 @@ def get_all(self):
         #self.get_averaging_type()
         #self.get_autorange()
                 
-    def _current_mode_get(self, par, mode=None):
+    def _current_mode_get(self, par, mode=None, parser=None):
         cmd=self._mode_par(mode, par)
-        return self.ask(cmd)
+        r=self.ask(cmd)        
+        if parser is not None:
+            r=parser(r)
+        return r
 
-    def _current_mode_set(self, par, val, mode=None):
-        cmd=self._mode_par_value(mode, par, val)
+    def _current_mode_set(self, value, par, mode=None):
+        cmd=self._mode_par_value(mode, par, value)
         return self.write(cmd)
 
 
@@ -236,7 +262,7 @@ def set_defaults(self):
         self.set_mode_volt_dc()
         self.digits.set(7)
         self.trigger_continuous.set(True)
-        self.range.set(10)
+        self.range.set(10) 
         self.nplc.set(1)
         self.averaging.set(False)
         return
@@ -252,6 +278,7 @@ def _determine_mode(self, mode):
         if mode not in self._modes and mode not in ('INIT', 'TRIG', 'SYST', 'DISP'):
             logging.warning('Invalid mode %s, assuming current' % mode)
             mode = self.mode.get_latest()
+        logging.debug('Determine mode: mode=%s' % mode)
         return mode
         
     def set_mode(self, mode):

From 287ec12dd55efec3e8478a4462714f11d9ac6960 Mon Sep 17 00:00:00 2001
From: Pieter 
Date: Thu, 14 Apr 2016 14:12:08 +0200
Subject: [PATCH 4/8] update docstring handling code

---
 docs/examples/Keithley_example.ipynb          | 326 ++++++++++++++++--
 qcodes/instrument/parameter.py                |  13 +-
 .../instrument_drivers/tektronix/Keithley.py  |  16 +-
 3 files changed, 326 insertions(+), 29 deletions(-)

diff --git a/docs/examples/Keithley_example.ipynb b/docs/examples/Keithley_example.ipynb
index e36dbec5286e..ba3400e54cb7 100644
--- a/docs/examples/Keithley_example.ipynb
+++ b/docs/examples/Keithley_example.ipynb
@@ -9,11 +9,249 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 1,
    "metadata": {
     "collapsed": false
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "application/javascript": [
+       "/*\r\n",
+       " * Qcodes Jupyter/IPython widgets\r\n",
+       " */\r\n",
+       "require([\r\n",
+       "    'nbextensions/widgets/widgets/js/widget',\r\n",
+       "    'nbextensions/widgets/widgets/js/manager'\r\n",
+       "], function (widget, manager) {\r\n",
+       "\r\n",
+       "    var UpdateView = widget.DOMWidgetView.extend({\r\n",
+       "        render: function() {\r\n",
+       "            window.MYWIDGET = this;\r\n",
+       "            this._interval = 0;\r\n",
+       "            this.update();\r\n",
+       "        },\r\n",
+       "        update: function() {\r\n",
+       "            this.display(this.model.get('_message'));\r\n",
+       "            this.setInterval();\r\n",
+       "        },\r\n",
+       "        display: function(message) {\r\n",
+       "            /*\r\n",
+       "             * display method: override this for custom display logic\r\n",
+       "             */\r\n",
+       "            this.el.innerHTML = message;\r\n",
+       "        },\r\n",
+       "        remove: function() {\r\n",
+       "            clearInterval(this._updater);\r\n",
+       "        },\r\n",
+       "        setInterval: function(newInterval) {\r\n",
+       "            var me = this;\r\n",
+       "            if(newInterval===undefined) newInterval = me.model.get('interval');\r\n",
+       "            if(newInterval===me._interval) return;\r\n",
+       "\r\n",
+       "            me._interval = newInterval;\r\n",
+       "\r\n",
+       "            if(me._updater) clearInterval(me._updater);\r\n",
+       "\r\n",
+       "            if(me._interval) {\r\n",
+       "                me._updater = setInterval(function() {\r\n",
+       "                    me.send({myupdate: true});\r\n",
+       "                    if(!me.model.comm_live) {\r\n",
+       "                        console.log('missing comm, canceling widget updates', me);\r\n",
+       "                        clearInterval(me._updater);\r\n",
+       "                    }\r\n",
+       "                }, me._interval * 1000);\r\n",
+       "            }\r\n",
+       "        }\r\n",
+       "    });\r\n",
+       "    manager.WidgetManager.register_widget_view('UpdateView', UpdateView);\r\n",
+       "\r\n",
+       "    var HiddenUpdateView = UpdateView.extend({\r\n",
+       "        display: function(message) {\r\n",
+       "            this.$el.hide();\r\n",
+       "        }\r\n",
+       "    });\r\n",
+       "    manager.WidgetManager.register_widget_view('HiddenUpdateView', HiddenUpdateView);\r\n",
+       "\r\n",
+       "    var SubprocessView = UpdateView.extend({\r\n",
+       "        render: function() {\r\n",
+       "            var me = window.SPVIEW = this;\r\n",
+       "            me._interval = 0;\r\n",
+       "            me._minimize = '';\r\n",
+       "            me._restore = '';\r\n",
+       "\r\n",
+       "            // in case there is already an outputView present,\r\n",
+       "            // like from before restarting the kernel\r\n",
+       "            $('.qcodes-output-view').not(me.$el).remove();\r\n",
+       "\r\n",
+       "            me.$el\r\n",
+       "                .addClass('qcodes-output-view')\r\n",
+       "                .attr('qcodes-state', 'docked')\r\n",
+       "                .html(\r\n",
+       "                    '
' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '
' +\r\n", + " '
'\r\n",
+       "                );\r\n",
+       "\r\n",
+       "            me.clearButton = me.$el.find('.qcodes-clear-output');\r\n",
+       "            me.minButton = me.$el.find('.qcodes-minimize');\r\n",
+       "            me.outputArea = me.$el.find('pre');\r\n",
+       "            me.subprocessList = me.$el.find('span');\r\n",
+       "            me.abortButton = me.$el.find('.qcodes-abort-loop');\r\n",
+       "\r\n",
+       "            me.clearButton.click(function() {\r\n",
+       "                me.outputArea.html('');\r\n",
+       "                me.clearButton.addClass('disabled');\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.abortButton.click(function() {\r\n",
+       "                me.send({abort: true});\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.$el.find('.js-state').click(function() {\r\n",
+       "                var oldState = me.$el.attr('qcodes-state'),\r\n",
+       "                    state = this.className.substr(this.className.indexOf('qcodes'))\r\n",
+       "                        .split('-')[1].split(' ')[0];\r\n",
+       "\r\n",
+       "                // not sure why I can't pop it out of the widgetarea in render, but it seems that\r\n",
+       "                // some other bit of code resets the parent after render if I do it there.\r\n",
+       "                // To be safe, just do it on every state click.\r\n",
+       "                me.$el.appendTo('body');\r\n",
+       "\r\n",
+       "                if(oldState === 'floated') {\r\n",
+       "                    me.$el.draggable('destroy').css({left:'', top: ''});\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                me.$el.attr('qcodes-state', state);\r\n",
+       "\r\n",
+       "                if(state === 'floated') {\r\n",
+       "                    me.$el.draggable().css({\r\n",
+       "                        left: window.innerWidth - me.$el.width() - 15,\r\n",
+       "                        top: window.innerHeight - me.$el.height() - 10\r\n",
+       "                    });\r\n",
+       "                }\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            $(window).resize(function() {\r\n",
+       "                if(me.$el.attr('qcodes-state') === 'floated') {\r\n",
+       "                    var position = me.$el.position(),\r\n",
+       "                        minVis = 20,\r\n",
+       "                        maxLeft = window.innerWidth - minVis,\r\n",
+       "                        maxTop = window.innerHeight - minVis;\r\n",
+       "\r\n",
+       "                    if(position.left > maxLeft) me.$el.css('left', maxLeft);\r\n",
+       "                    if(position.top > maxTop) me.$el.css('top', maxTop);\r\n",
+       "                }\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.update();\r\n",
+       "        },\r\n",
+       "\r\n",
+       "        display: function(message) {\r\n",
+       "            if(message) {\r\n",
+       "                var initialScroll = this.outputArea.scrollTop();\r\n",
+       "                this.outputArea.scrollTop(this.outputArea.prop('scrollHeight'));\r\n",
+       "                var scrollBottom = this.outputArea.scrollTop();\r\n",
+       "\r\n",
+       "                if(this.$el.attr('qcodes-state') === 'minimized') {\r\n",
+       "                    this.$el.find('.qcodes-docked').click();\r\n",
+       "                    // always scroll to the bottom if we're restoring\r\n",
+       "                    // because of a new message\r\n",
+       "                    initialScroll = scrollBottom;\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                this.outputArea.append(message);\r\n",
+       "                this.clearButton.removeClass('disabled');\r\n",
+       "\r\n",
+       "                // if we were scrolled to the bottom initially, make sure\r\n",
+       "                // we stay that way.\r\n",
+       "                this.outputArea.scrollTop(initialScroll === scrollBottom ?\r\n",
+       "                    this.outputArea.prop('scrollHeight') : initialScroll);\r\n",
+       "            }\r\n",
+       "\r\n",
+       "            var processes = this.model.get('_processes') || 'No subprocesses';\r\n",
+       "            this.abortButton.toggleClass('disabled', processes.indexOf('Measurement')===-1);\r\n",
+       "            this.subprocessList.text(processes);\r\n",
+       "        }\r\n",
+       "    });\r\n",
+       "    manager.WidgetManager.register_widget_view('SubprocessView', SubprocessView);\r\n",
+       "});\r\n"
+      ],
+      "text/plain": [
+       ""
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/html": [
+       ""
+      ],
+      "text/plain": [
+       ""
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
    "source": [
     "import matplotlib.pyplot as plt\n",
     "import time\n",
@@ -44,7 +282,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 2,
    "metadata": {
     "collapsed": false
    },
@@ -55,7 +293,7 @@
        ""
       ]
      },
-     "execution_count": 16,
+     "execution_count": 2,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -67,7 +305,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 3,
    "metadata": {
     "collapsed": false
    },
@@ -76,7 +314,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Connected to:  KEITHLEY INSTRUMENTS INC., MODEL 2700, 0792116, B06  /A02    in 0.14s\n"
+      "Connected to:  KEITHLEY INSTRUMENTS INC., MODEL 2700, 0792116, B06  /A02    in 0.20s\n"
      ]
     }
    ],
@@ -87,7 +325,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 4,
    "metadata": {
     "collapsed": false
    },
@@ -106,7 +344,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 5,
    "metadata": {
     "collapsed": false,
     "scrolled": true
@@ -116,12 +354,12 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "measure: -1.044276\n",
-      "measure: -1.050299\n",
-      "measure: -1.056594\n",
-      "measure: -1.069865\n",
-      "measure: -1.075954\n",
-      "measure: -1.081997\n"
+      "measure: -0.095112\n",
+      "measure: -0.104225\n",
+      "measure: -0.112567\n",
+      "measure: -0.129525\n",
+      "measure: -0.137803\n",
+      "measure: -0.146068\n"
      ]
     }
    ],
@@ -132,7 +370,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 6,
    "metadata": {
     "collapsed": false
    },
@@ -143,7 +381,7 @@
        "[]"
       ]
      },
-     "execution_count": 12,
+     "execution_count": 6,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -160,7 +398,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 7,
    "metadata": {
     "collapsed": false
    },
@@ -171,7 +409,7 @@
        "['VOLT:DC']"
       ]
      },
-     "execution_count": 13,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -182,7 +420,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 23,
+   "execution_count": 8,
    "metadata": {
     "collapsed": false
    },
@@ -191,7 +429,13 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "sds\n"
+      "Get integration time in Number of PowerLine Cycles.\n",
+      "To get the integrationtime in seconds, use get_integrationtime().\r\n",
+      "Parameter class:\n",
+      "* `name` nplc\r\n",
+      "* `label` nplc\r\n",
+      "* `units` APER\r\n",
+      "\n"
      ]
     }
    ],
@@ -199,6 +443,48 @@
     "print(k1.nplc.__doc__)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Test whether the object can be pickled"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
+    {
+     "ename": "ValueError",
+     "evalue": "ctypes objects containing pointers cannot be pickled",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
+      "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mtempfile\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[1;32mwith\u001b[0m \u001b[0mtempfile\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mTemporaryFile\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'wb'\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0moutput\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m     \u001b[0mpickle\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdump\u001b[0m\u001b[1;33m(\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mk1\u001b[0m\u001b[1;33m,\u001b[0m  \u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0moutput\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[1;31mValueError\u001b[0m: ctypes objects containing pointers cannot be pickled"
+     ]
+    }
+   ],
+   "source": [
+    "import pickle\n",
+    "import tempfile\n",
+    "with tempfile.TemporaryFile('wb') as output:\n",
+    "    pickle.dump( (k1,  ), output)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": []
+  },
   {
    "cell_type": "code",
    "execution_count": null,
diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py
index da76b5ebfd45..6121e7fdce6a 100644
--- a/qcodes/instrument/parameter.py
+++ b/qcodes/instrument/parameter.py
@@ -37,6 +37,7 @@
 import time
 import asyncio
 import logging
+import os
 
 from qcodes.utils.helpers import (permissive_range, wait_secs,
                                   DelegateAttributes)
@@ -167,9 +168,19 @@ def __init__(self,
             self.setpoint_names = setpoint_names
             self.setpoint_labels = setpoint_labels
 
+
+        # generate default docstring
+        self.__doc__ = 'Parameter class:\n* `name` %s' % self.name + os.linesep
+        self.__doc__ += '* `label` %s' % self.label + os.linesep
+        self.__doc__ += '* `units` %s' % self.units + os.linesep
+        #parameter label (if not None)
+        #units
+        #validator type
+        #validator min and max (if applicable)
+
         if docstring is not None:
             #logging.debug('add docstring to Parameter!')
-            self.__doc__ = docstring
+            self.__doc__ = docstring + os.linesep + self.__doc__
             
         # record of latest value and when it was set or measured
         # what exactly this means is different for different subclasses
diff --git a/qcodes/instrument_drivers/tektronix/Keithley.py b/qcodes/instrument_drivers/tektronix/Keithley.py
index 47c3345650ed..bb660dec66c4 100644
--- a/qcodes/instrument_drivers/tektronix/Keithley.py
+++ b/qcodes/instrument_drivers/tektronix/Keithley.py
@@ -80,7 +80,6 @@ def __init__(self, name, address, reset=False):
         t0 = time.time()
         super().__init__(name, address)
 
-        self._modes = ['']
         self.add_parameter('IDN', get_cmd='*IDN?')
         
         self._modes = ['VOLT:AC', 'VOLT:DC', 'CURR:AC', 'CURR:DC', 'RES',
@@ -90,6 +89,7 @@ def __init__(self, name, address, reset=False):
         self._averaging_types = ['MOV','REP']
         self._trigger_sent = False
         
+        
         # Add parameters to wrapper
         self.add_parameter('mode', get_cmd=':CONF?', get_parser=parsestr, set_cmd=':CONF:{}', vals=StringValidator() )
 
@@ -112,11 +112,11 @@ def __init__(self, name, address, reset=False):
         
         self.add_parameter('nplc', get_cmd=partial( self._current_mode_get, 'NPLC', parser=float),
                        set_cmd=partial(self._current_mode_set, par='NPLC', mode=None), units='APER',
-                     docstring='Get integration time in Number of PowerLine Cycles. \
-            To get the integrationtime in seconds, use get_integrationtime().'  )
+                     docstring='Get integration time in Number of PowerLine Cycles.' +
+            '\nTo get the integrationtime in seconds, use get_integrationtime().'  )
 
         self.add_parameter('range', get_cmd=partial(self._current_mode_get, 'RANG',parser=float),
-                   set_cmd=partial(self._current_mode_set, par='RANG'), units='RANG'  )
+                   set_cmd=partial(self._current_mode_set, par='RANG'), units='RANG', docstring='Sets the measurement range. Note that not only a discrete set of ranges can be set (see the manual for details).'  )
 
         self.add_parameter('integrationtime', get_cmd=partial(self._current_mode_get, 'APER', parser=float),
                 set_cmd=partial(self._current_mode_set, par='APER', mode=None), units='s',
@@ -134,15 +134,15 @@ def __init__(self, name, address, reset=False):
             flags=Instrument.FLAG_GETSET,
             units='s', minval=0.001, maxval=99999.999, type=float)
         self.add_parameter('readval', flags=Instrument.FLAG_GET,
-            units='AU',
+            units='arb.unit',
             type=float,
             tags=['measure'])
         self.add_parameter('readlastval', flags=Instrument.FLAG_GET,
-            units='AU',
+            units='arb.unit',
             type=float,
             tags=['measure'])
         self.add_parameter('readnextval', flags=Instrument.FLAG_GET,
-            units='AU',
+            units='arb.unit',
             type=float,
             tags=['measure'])
         self.add_parameter('nplc',
@@ -169,7 +169,7 @@ def __init__(self, name, address, reset=False):
             
 
         # add functions
-        self.add_function('readnext', units='AU', call_cmd=':DATA:FRESH?', return_parser=float)
+        self.add_function('readnext', units='arb.unit', call_cmd=':DATA:FRESH?', return_parser=float)
 
         if reset:
             self.reset()

From cea5b7cada1bdc4f4fc6d117b7295cfc18680434 Mon Sep 17 00:00:00 2001
From: Pieter 
Date: Thu, 14 Apr 2016 14:26:26 +0200
Subject: [PATCH 5/8] rename Keithley driver

---
 docs/examples/Keithley_example.ipynb                            | 2 +-
 .../tektronix/{Keithley.py => Keithley_2700.py}                 | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename qcodes/instrument_drivers/tektronix/{Keithley.py => Keithley_2700.py} (100%)

diff --git a/docs/examples/Keithley_example.ipynb b/docs/examples/Keithley_example.ipynb
index ba3400e54cb7..fe71ad91c260 100644
--- a/docs/examples/Keithley_example.ipynb
+++ b/docs/examples/Keithley_example.ipynb
@@ -300,7 +300,7 @@
    ],
    "source": [
     "import qcodes.instrument_drivers\n",
-    "import qcodes.instrument_drivers.tektronix.Keithley as keith; reload(keith)"
+    "import qcodes.instrument_drivers.tektronix.Keithley_2700 as keith; reload(keith)"
    ]
   },
   {
diff --git a/qcodes/instrument_drivers/tektronix/Keithley.py b/qcodes/instrument_drivers/tektronix/Keithley_2700.py
similarity index 100%
rename from qcodes/instrument_drivers/tektronix/Keithley.py
rename to qcodes/instrument_drivers/tektronix/Keithley_2700.py

From 76dc2750e2b47b0a52e1b8680e7cc6e2af64279f Mon Sep 17 00:00:00 2001
From: alexcjohnson 
Date: Sat, 16 Apr 2016 01:44:26 +0200
Subject: [PATCH 6/8] docstring for multi-return parameters, and some
 parameter/function linting

---
 qcodes/instrument/function.py  |  7 ++++---
 qcodes/instrument/parameter.py | 31 +++++++++++++++++--------------
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/qcodes/instrument/function.py b/qcodes/instrument/function.py
index ca1b1fa67c77..ee938d53b1a5 100644
--- a/qcodes/instrument/function.py
+++ b/qcodes/instrument/function.py
@@ -47,7 +47,8 @@ class Function(Metadatable):
     NOTE: parsers only apply if call_cmd is a string. The function forms
         of call_cmd and async_call_cmd should do their own parsing.
     docstring: documentation string for the __doc__ field of the object
-        The __doc__ field of the instance is used by some help systems, but not all 
+        The __doc__ field of the instance is used by some help systems,
+        but not all (particularly not builtin `help(...)`)
     '''
     def __init__(self, name, instrument=None,
                  call_cmd=None, async_call_cmd=None,
@@ -59,8 +60,8 @@ def __init__(self, name, instrument=None,
         self.name = name
 
         if docstring is not None:
-            self.__doc__=docstring
-            
+            self.__doc__ = docstring
+
         self._set_args(args)
         self._set_call(call_cmd, async_call_cmd,
                        arg_parser, return_parser)
diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py
index 6121e7fdce6a..d596321ae5d1 100644
--- a/qcodes/instrument/parameter.py
+++ b/qcodes/instrument/parameter.py
@@ -145,7 +145,13 @@ def __init__(self,
             # and names are the items it returns
             self.names = names
             self.labels = names if labels is None else names
-            self.units = units if units is not None else ['']*len(names)
+            self.units = units if units is not None else [''] * len(names)
+
+            self.__doc__ = os.linesep.join((
+                'Parameter class:',
+                '* `names` %s' % ', '.join(self.names),
+                '* `labels` %s' % ', '.join(self.labels),
+                '* `units` %s' % ', '.join(self.units)))
 
         elif name is not None:
             self.name = name
@@ -155,6 +161,14 @@ def __init__(self,
             # vals / validate only applies to simple single-value parameters
             self._set_vals(vals)
 
+            # generate default docstring
+            self.__doc__ = os.linesep.join((
+                'Parameter class:',
+                '* `name` %s' % self.name,
+                '* `label` %s' % self.label,
+                '* `units` %s' % self.units,
+                '* `vals` %s' % repr(self._vals)))
+
         else:
             raise ValueError('either name or names is required')
 
@@ -168,20 +182,9 @@ def __init__(self,
             self.setpoint_names = setpoint_names
             self.setpoint_labels = setpoint_labels
 
-
-        # generate default docstring
-        self.__doc__ = 'Parameter class:\n* `name` %s' % self.name + os.linesep
-        self.__doc__ += '* `label` %s' % self.label + os.linesep
-        self.__doc__ += '* `units` %s' % self.units + os.linesep
-        #parameter label (if not None)
-        #units
-        #validator type
-        #validator min and max (if applicable)
-
         if docstring is not None:
-            #logging.debug('add docstring to Parameter!')
             self.__doc__ = docstring + os.linesep + self.__doc__
-            
+
         # record of latest value and when it was set or measured
         # what exactly this means is different for different subclasses
         # but they all use the same attributes so snapshot is consistent.
@@ -285,7 +288,7 @@ class StandardParameter(Parameter):
     max_val_age: max time (in seconds) to trust a saved value from
         this parameter as the starting point of a sweep
     docstring: documentation string for the __doc__ field of the object
-        The __doc__ field of the instance is used by some help systems, but not all 
+        The __doc__ field of the instance is used by some help systems, but not all
     '''
     def __init__(self, name, instrument=None,
                  get_cmd=None, async_get_cmd=None, get_parser=None,

From bd4ae885ab31bb2275ee21330471da4907bfc5b5 Mon Sep 17 00:00:00 2001
From: alexcjohnson 
Date: Sat, 16 Apr 2016 02:09:50 +0200
Subject: [PATCH 7/8] lint K2700 driver

---
 .../tektronix/Keithley_2700.py                | 227 +++++++++++-------
 1 file changed, 134 insertions(+), 93 deletions(-)

diff --git a/qcodes/instrument_drivers/tektronix/Keithley_2700.py b/qcodes/instrument_drivers/tektronix/Keithley_2700.py
index bb660dec66c4..f89f6b47c377 100644
--- a/qcodes/instrument_drivers/tektronix/Keithley_2700.py
+++ b/qcodes/instrument_drivers/tektronix/Keithley_2700.py
@@ -23,45 +23,45 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 import time
-from numpy import pi
+import logging
+from functools import partial
+
 from qcodes.instrument.visa import VisaInstrument
-#from qcodes.utils import validators
 from qcodes.utils.validators import Strings as StringValidator
 from qcodes.utils.validators import Ints as IntsValidator
 from qcodes.utils.validators import Numbers as NumbersValidator
 
-import logging
 
-#%% Helper functions
+# %% Helper functions
+
 
 def bool_to_str(val):
     '''
     Function to convert boolean to 'ON' or 'OFF'
     '''
-    if val == True:
+    if val is True:
         return "ON"
     else:
         return "OFF"
 
 
+# %% Driver for Keithley_2700
 
-#%% Driver for Keithley_2700       
-
-from functools import partial
-        
 def parseint(v):
     logging.debug('parseint: %s -> %d' % (v, int(v)))
     return int(v)
 
+
 def parsebool(v):
-    r=bool(int(v))
+    r = bool(int(v))
     logging.debug('parsetobool: %s -> %d' % (v, r))
     return r
 
+
 def parsestr(v):
     return v.strip().strip('"')
-            
-            
+
+
 class Keithley_2700(VisaInstrument):
     '''
     This is the qcodes driver for the Keithley_2700 Multimeter
@@ -71,58 +71,96 @@ class Keithley_2700(VisaInstrument):
             change_display=, change_autozero=)
 
     Status: beta-version.
-    
+
     This driver will most likely work for multiple Keithley SourceMeters.
 
-    This driver does not contain all commands available, but only the ones most commonly used.
+    This driver does not contain all commands available, but only the ones
+    most commonly used.
     '''
     def __init__(self, name, address, reset=False):
         t0 = time.time()
         super().__init__(name, address)
 
         self.add_parameter('IDN', get_cmd='*IDN?')
-        
+
         self._modes = ['VOLT:AC', 'VOLT:DC', 'CURR:AC', 'CURR:DC', 'RES',
-            'FRES', 'TEMP', 'FREQ']
-        #self._change_display = change_display
-        #self._change_autozero = change_autozero
-        self._averaging_types = ['MOV','REP']
+                       'FRES', 'TEMP', 'FREQ']
+        # self._change_display = change_display
+        # self._change_autozero = change_autozero
+        self._averaging_types = ['MOV', 'REP']
         self._trigger_sent = False
-        
-        
+
         # Add parameters to wrapper
-        self.add_parameter('mode', get_cmd=':CONF?', get_parser=parsestr, set_cmd=':CONF:{}', vals=StringValidator() )
-
-        self.add_parameter('trigger_count', get_cmd=self._mode_par('INIT', 'CONT'),
-                           get_parser=int, set_cmd=self._mode_par_value('INIT','CONT','{}'),
-                                vals=IntsValidator(), units='#' )
-        self.add_parameter('trigger_delay', get_cmd=self._mode_par('TRIG', 'DEL'),
-                   get_parser=float, set_cmd=self._mode_par_value('TRIG', 'DEL', '{}'),
-                      vals=NumbersValidator(min_value=0, max_value=999999.999), units='s')
-
-        self.add_parameter('trigger_continuous', get_cmd=self._mode_par('INIT', 'CONT'),
-                   get_parser=parsebool, set_cmd=self._mode_par_value('INIT', 'CONT', '{}'), set_parser=bool_to_str )
-        
-        self.add_parameter('averaging', get_cmd=partial(self._current_mode_get, 'AVER:STAT', parser=parsebool),
-                   set_cmd=partial(self._current_mode_set, par='AVER:STAT'),
-                   set_parser=bool_to_str )
-
-        self.add_parameter('digits', get_cmd=partial(self._current_mode_get, 'DIG', parser=int),
-                   set_cmd=partial(self._current_mode_set, par='DIG')  )
-        
-        self.add_parameter('nplc', get_cmd=partial( self._current_mode_get, 'NPLC', parser=float),
-                       set_cmd=partial(self._current_mode_set, par='NPLC', mode=None), units='APER',
-                     docstring='Get integration time in Number of PowerLine Cycles.' +
-            '\nTo get the integrationtime in seconds, use get_integrationtime().'  )
-
-        self.add_parameter('range', get_cmd=partial(self._current_mode_get, 'RANG',parser=float),
-                   set_cmd=partial(self._current_mode_set, par='RANG'), units='RANG', docstring='Sets the measurement range. Note that not only a discrete set of ranges can be set (see the manual for details).'  )
-
-        self.add_parameter('integrationtime', get_cmd=partial(self._current_mode_get, 'APER', parser=float),
-                set_cmd=partial(self._current_mode_set, par='APER', mode=None), units='s',
-                vals=NumbersValidator(min_value=2e-4, max_value=1.),
-                docstring='Get integration time in seconds. To get the integrationtime as a Number of PowerLine Cycles, use get_nplc().' )
- 
+        self.add_parameter('mode',
+                           get_cmd=':CONF?',
+                           get_parser=parsestr,
+                           set_cmd=':CONF:{}',
+                           vals=StringValidator())
+
+        self.add_parameter('trigger_count',
+                           get_cmd=self._mode_par('INIT', 'CONT'),
+                           get_parser=int,
+                           set_cmd=self._mode_par_value('INIT', 'CONT', '{}'),
+                           vals=IntsValidator(),
+                           units='#')
+        self.add_parameter('trigger_delay',
+                           get_cmd=self._mode_par('TRIG', 'DEL'),
+                           get_parser=float,
+                           set_cmd=self._mode_par_value('TRIG', 'DEL', '{}'),
+                           vals=NumbersValidator(min_value=0,
+                                                 max_value=999999.999),
+                           units='s')
+
+        self.add_parameter('trigger_continuous',
+                           get_cmd=self._mode_par('INIT', 'CONT'),
+                           get_parser=parsebool,
+                           set_cmd=self._mode_par_value('INIT', 'CONT', '{}'),
+                           set_parser=bool_to_str)
+
+        self.add_parameter('averaging',
+                           get_cmd=partial(self._current_mode_get, 'AVER:STAT',
+                                           parser=parsebool),
+                           set_cmd=partial(self._current_mode_set,
+                                           par='AVER:STAT'),
+                           set_parser=bool_to_str)
+
+        self.add_parameter('digits',
+                           get_cmd=partial(self._current_mode_get, 'DIG',
+                                           parser=int),
+                           set_cmd=partial(self._current_mode_set, par='DIG'))
+
+        self.add_parameter('nplc',
+                           get_cmd=partial(self._current_mode_get, 'NPLC',
+                                           parser=float),
+                           set_cmd=partial(self._current_mode_set, par='NPLC',
+                                           mode=None),
+                           units='APER',
+                           docstring=('Get integration time in Number of '
+                                      'PowerLine Cycles.\n'
+                                      'To get the integrationtime in seconds, '
+                                      'use get_integrationtime().'))
+
+        self.add_parameter('range',
+                           get_cmd=partial(self._current_mode_get, 'RANG',
+                                           parser=float),
+                           set_cmd=partial(self._current_mode_set, par='RANG'),
+                           units='RANG',
+                           docstring=('Sets the measurement range.\n'
+                                      'Note that not only a discrete set of '
+                                      'ranges can be set (see the manual for '
+                                      'details).'))
+
+        self.add_parameter('integrationtime',
+                           get_cmd=partial(self._current_mode_get, 'APER',
+                                           parser=float),
+                           set_cmd=partial(self._current_mode_set, par='APER',
+                                           mode=None),
+                           units='s',
+                           vals=NumbersValidator(min_value=2e-4, max_value=1.),
+                           docstring=('Get integration time in seconds.\n'
+                                      'To get the integrationtime as a Number '
+                                      'of PowerLine Cycles, use get_nplc().'))
+
         '''
         self.add_parameter('range',
             flags=Instrument.FLAG_GETSET,
@@ -166,10 +204,12 @@ def __init__(self, name, address, reset=False):
             units='',
             type=bool)
         '''
-            
 
         # add functions
-        self.add_function('readnext', units='arb.unit', call_cmd=':DATA:FRESH?', return_parser=float)
+        self.add_function('readnext',
+                          units='arb.unit',
+                          call_cmd=':DATA:FRESH?',
+                          return_parser=float)
 
         if reset:
             self.reset()
@@ -182,7 +222,6 @@ def __init__(self, name, address, reset=False):
               self.get('IDN').replace(',', ', ').replace('\n', ' '),
               'in %.2fs' % (t1-t0))
 
-
     def get_all(self):
         '''
         Reads all relevant parameters from instrument
@@ -194,38 +233,38 @@ def get_all(self):
             None
         '''
         logging.info('Get all relevant data from device')
-        
-        for p in ['mode', 'trigger_count', 'trigger_continuous', 'averaging', 'digits', 'nplc', 'integrationtime']:
+
+        for p in ['mode', 'trigger_count', 'trigger_continuous', 'averaging',
+                  'digits', 'nplc', 'integrationtime']:
             logging.debug('get %s' % p)
-            par=getattr(self,p)
+            par = getattr(self, p)
             par.get()
-            
-#        self.get_range()
- #       self.get_trigger_delay()
-  #      self.get_trigger_source()
-   #     self.get_trigger_timer()
-      #  self.get_display()
-       # self.get_autozero()
-        #self.get_averaging_window()
-        #self.get_averaging_count()
-        #self.get_averaging_type()
-        #self.get_autorange()
-                
+
+        # self.get_range()
+        # self.get_trigger_delay()
+        # self.get_trigger_source()
+        # self.get_trigger_timer()
+        # self.get_display()
+        # self.get_autozero()
+        # self.get_averaging_window()
+        # self.get_averaging_count()
+        # self.get_averaging_type()
+        # self.get_autorange()
+
     def _current_mode_get(self, par, mode=None, parser=None):
-        cmd=self._mode_par(mode, par)
-        r=self.ask(cmd)        
+        cmd = self._mode_par(mode, par)
+        r = self.ask(cmd)
         if parser is not None:
-            r=parser(r)
+            r = parser(r)
         return r
 
     def _current_mode_set(self, value, par, mode=None):
-        cmd=self._mode_par_value(mode, par, value)
+        cmd = self._mode_par_value(mode, par, value)
         return self.write(cmd)
 
-
-# --------------------------------------
-#           functions
-# --------------------------------------
+    # --------------------------------------
+    #           functions
+    # --------------------------------------
 
     def set_mode_volt_dc(self):
         '''
@@ -239,7 +278,7 @@ def set_mode_volt_dc(self):
         '''
         logging.debug('Set mode to DC Voltage')
         self.mode.set('VOLT:DC')
-        
+
     def set_defaults(self):
         '''
         Set to driver defaults:
@@ -254,19 +293,19 @@ def set_defaults(self):
 
         self.write('SYST:PRES')
         self.write(':FORM:ELEM READ')
-            # Sets the format to only the read out, all options are:
-            # READing = DMM reading, UNITs = Units,
-            # TSTamp = Timestamp, RNUMber = Reading number,
-            # CHANnel = Channel number, LIMits = Limits reading
+        # Sets the format to only the read out, all options are:
+        # READing = DMM reading, UNITs = Units,
+        # TSTamp = Timestamp, RNUMber = Reading number,
+        # CHANnel = Channel number, LIMits = Limits reading
 
         self.set_mode_volt_dc()
         self.digits.set(7)
         self.trigger_continuous.set(True)
-        self.range.set(10) 
+        self.range.set(10)
         self.nplc.set(1)
         self.averaging.set(False)
         return
-        
+
     def _determine_mode(self, mode):
         '''
         Return the mode string to use.
@@ -274,13 +313,14 @@ def _determine_mode(self, mode):
         '''
         logging.debug('Determine mode with mode=%s' % mode)
         if mode is None:
-            mode = self.mode.get_latest() # _mode(query=False)
-        if mode not in self._modes and mode not in ('INIT', 'TRIG', 'SYST', 'DISP'):
+            mode = self.mode.get_latest()  # _mode(query=False)
+        if mode not in self._modes and mode not in ('INIT', 'TRIG', 'SYST',
+                                                    'DISP'):
             logging.warning('Invalid mode %s, assuming current' % mode)
             mode = self.mode.get_latest()
         logging.debug('Determine mode: mode=%s' % mode)
         return mode
-        
+
     def set_mode(self, mode):
         '''
         Set the mode to the specified value
@@ -309,14 +349,14 @@ def set_mode(self, mode):
         else:
             logging.error('invalid mode %s' % mode)
 
+        # Get all values again because some parameters depend on mode
         self.get_all()
-            # Get all values again because some parameters depend on mode
 
     def _mode_par_value(self, mode, par, val):
         '''
         For internal use only!!
         Create command string based on current mode
-        
+
         Input:
             mode (string) : The mode to use
             par (string)  : Parameter
@@ -328,11 +368,12 @@ def _mode_par_value(self, mode, par, val):
         mode = self._determine_mode(mode)
         string = ':%s:%s %s' % (mode, par, val)
         return string
+
     def _mode_par(self, mode, par):
         '''
         For internal use only!!
         Create command string based on current mode
-        
+
         Input:
             mode (string) : The mode to use
             par (string)  : Parameter
@@ -344,7 +385,7 @@ def _mode_par(self, mode, par):
         mode = self._determine_mode(mode)
         string = ':%s:%s?' % (mode, par, )
         return string
-        
+
     def reset(self):
         '''
         Resets instrument to default values

From b6f8ee271171fdc55fc25cbe1be5e4c53a506e7b Mon Sep 17 00:00:00 2001
From: Pieter 
Date: Mon, 18 Apr 2016 13:34:53 +0200
Subject: [PATCH 8/8] fix bool_to_str function

---
 docs/examples/Keithley_example.ipynb          | 10 ++++----
 .../tektronix/Keithley_2700.py                | 25 ++++++-------------
 2 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/docs/examples/Keithley_example.ipynb b/docs/examples/Keithley_example.ipynb
index fe71ad91c260..1d9799c229f8 100644
--- a/docs/examples/Keithley_example.ipynb
+++ b/docs/examples/Keithley_example.ipynb
@@ -282,7 +282,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 12,
    "metadata": {
     "collapsed": false
    },
@@ -290,10 +290,10 @@
     {
      "data": {
       "text/plain": [
-       ""
+       ""
       ]
      },
-     "execution_count": 2,
+     "execution_count": 12,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -305,7 +305,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 13,
    "metadata": {
     "collapsed": false
    },
@@ -314,7 +314,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Connected to:  KEITHLEY INSTRUMENTS INC., MODEL 2700, 0792116, B06  /A02    in 0.20s\n"
+      "Connected to:  KEITHLEY INSTRUMENTS INC., MODEL 2700, 0792116, B06  /A02    in 0.17s\n"
      ]
     }
    ],
diff --git a/qcodes/instrument_drivers/tektronix/Keithley_2700.py b/qcodes/instrument_drivers/tektronix/Keithley_2700.py
index f89f6b47c377..39e11a2b8a04 100644
--- a/qcodes/instrument_drivers/tektronix/Keithley_2700.py
+++ b/qcodes/instrument_drivers/tektronix/Keithley_2700.py
@@ -39,7 +39,7 @@ def bool_to_str(val):
     '''
     Function to convert boolean to 'ON' or 'OFF'
     '''
-    if val is True:
+    if val:
         return "ON"
     else:
         return "OFF"
@@ -116,6 +116,11 @@ def __init__(self, name, address, reset=False):
                            get_parser=parsebool,
                            set_cmd=self._mode_par_value('INIT', 'CONT', '{}'),
                            set_parser=bool_to_str)
+        self.add_parameter('display',
+                           get_cmd=self._mode_par('DISP', 'ENAB'),
+                           get_parser=parsebool,
+                           set_cmd=self._mode_par_value('DISP', 'ENAB', '{}'),
+                           set_parser=bool_to_str)
 
         self.add_parameter('averaging',
                            get_cmd=partial(self._current_mode_get, 'AVER:STAT',
@@ -162,9 +167,6 @@ def __init__(self, name, address, reset=False):
                                       'of PowerLine Cycles, use get_nplc().'))
 
         '''
-        self.add_parameter('range',
-            flags=Instrument.FLAG_GETSET,
-            units='', minval=0.1, maxval=1000, type=float)
         self.add_parameter('trigger_source',
             flags=Instrument.FLAG_GETSET,
             units='')
@@ -183,11 +185,6 @@ def __init__(self, name, address, reset=False):
             units='arb.unit',
             type=float,
             tags=['measure'])
-        self.add_parameter('nplc',
-            flags=Instrument.FLAG_GETSET,
-            units='#', type=float, minval=0.01, maxval=50)
-        self.add_parameter('display', flags=Instrument.FLAG_GETSET,
-            type=bool)
         self.add_parameter('autozero', flags=Instrument.FLAG_GETSET,
             type=bool)
         self.add_parameter('averaging_window',
@@ -235,16 +232,14 @@ def get_all(self):
         logging.info('Get all relevant data from device')
 
         for p in ['mode', 'trigger_count', 'trigger_continuous', 'averaging',
-                  'digits', 'nplc', 'integrationtime']:
+                  'digits', 'nplc', 'integrationtime', 'range', 'display']:
             logging.debug('get %s' % p)
             par = getattr(self, p)
             par.get()
 
-        # self.get_range()
         # self.get_trigger_delay()
         # self.get_trigger_source()
         # self.get_trigger_timer()
-        # self.get_display()
         # self.get_autozero()
         # self.get_averaging_window()
         # self.get_averaging_count()
@@ -400,8 +395,4 @@ def reset(self):
         self._visainstrument.write('*RST')
         self.get_all()
 
-    # def on(self):
-    #     self.set('status', 'on')
-
-    # def off(self):
-    #     self.set('status', 'off')
+    
\ No newline at end of file