pygpibtoolkit/HP3562A/q3562A.py

Sun, 17 Feb 2008 22:59:59 +0100

author
David Douard <david.douard@logilab.fr>
date
Sun, 17 Feb 2008 22:59:59 +0100
changeset 52
12dfcb77a590
parent 49
a682ee5e6a55
child 53
8e32c806fcdd
permissions
-rw-r--r--

added several example coord datablock & make use of some icons

#

import os, sys
import time
import glob

from PyQt4 import QtGui, QtCore, uic
from PyQt4.QtCore import SIGNAL, Qt

if "-m" in sys.argv:
    from dump_datablock_mockup import HP3562dumper
else: 
    from dump_datablock import HP3562dumper

from pygpibtoolkit.qt4.qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem
from pygpibtoolkit.qt4.qpreferences import PointItem, SizeItem, ByteArrayItem
from pygpibtoolkit.qt4.qpreferences import AbstractPreferences
from pygpibtoolkit.qt4.qpreferenceseditor import PreferencesEditor

from pygpibtoolkit.tools import str_num_cmp

import pygpibtoolkit.qt4.resources_rc
import pygpibtoolkit.qt4.icons_ressource_rc

from datablockwidget import getChild

class Preferences(AbstractPreferences):
    ORGANISATION="Logilab"
    APPLICATION="q3562"

    _pos = PointItem()
    _size = SizeItem()
    _appState = ByteArrayItem()

    device = UnicodeItem(default='/dev/ttyUSB0',
                         name=u'device',
                         description=u'GPIB device',
                         group="GPIB settings")
    address = IntItem(default=0, min=0, max=16,
                      name=u'GPIB address',
                      group="GPIB settings")
    
    background = ColorItem(default=QtGui.QColor("white"),
                           name="Plots background",
                           group="Colors")
    
class Qt3562(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.workspace = QtGui.QWorkspace()
        self.setCentralWidget(self.workspace)
        self.connect(self.workspace, SIGNAL("windowActivated(QWidget *)"),
                     self.updateMenus)
        self.windowMapper = QtCore.QSignalMapper(self)
        self.connect(self.windowMapper, QtCore.SIGNAL("mapped(QWidget *)"),
                     self.workspace, QtCore.SLOT("setActiveWindow(QWidget *)"))
        
        self._receiving = False
        
        self.createActions()
        self.createMenus()
        self.createToolBars()
        self.createStatusBar()
        self.updateMenus()

        self._prefs = Preferences()
        self.initializeGPIB()
        if self._prefs._pos:
            self.move(self._prefs._pos)
        if self._prefs._size:
            self.resize(self._prefs._size)
        if self._prefs._appState:
            self.restoreState(self._prefs._appState)
        self.readPreferences()

    def readPreferences(self):
        bg = self._prefs.background
        #pen_colors = [self._prefs["color%d"%i] for i in range(8)]
        #self.plotterWidget.pen_colors = pen_colors
                
    def createActions(self):
        self.openAct = QtGui.QAction(QtGui.QIcon(":/icons/open.png"),
                        self.tr("&Open..."), self)
        self.openAct.setShortcut(self.tr("Ctrl+O"))
        self.openAct.setStatusTip(self.tr("Open an existing data block file"))
        self.connect(self.openAct, QtCore.SIGNAL("triggered()"), self.open)

        self.saveAsAct = QtGui.QAction(self.tr("Save &As..."), self)
        self.saveAsAct.setStatusTip(self.tr("Save the document under a new name"))
        self.connect(self.saveAsAct, QtCore.SIGNAL("triggered()"), self.saveAs)

        self.exitAct = QtGui.QAction(self.tr("E&xit"), self)
        self.exitAct.setShortcut(self.tr("Ctrl+Q"))
        self.exitAct.setStatusTip(self.tr("Exit the application"))
        self.connect(self.exitAct, QtCore.SIGNAL("triggered()"), self.close)
                
        self.closeAct = QtGui.QAction(self.tr("Cl&ose"), self)
        self.closeAct.setShortcut(self.tr("Ctrl+W"))
        self.closeAct.setStatusTip(self.tr("Close the active window"))
        self.connect(self.closeAct, QtCore.SIGNAL("triggered()"),
                     self.workspace.closeActiveWindow)

        self.closeAllAct = QtGui.QAction(self.tr("Close &All"), self)
        self.closeAllAct.setStatusTip(self.tr("Close all the windows"))
        self.connect(self.closeAllAct, QtCore.SIGNAL("triggered()"),
                     self.workspace.closeAllWindows)

        self.tileAct = QtGui.QAction(self.tr("&Tile"), self)
        self.tileAct.setStatusTip(self.tr("Tile the windows"))
        self.connect(self.tileAct, QtCore.SIGNAL("triggered()"),
                     self.workspace.tile)

        self.cascadeAct = QtGui.QAction(self.tr("&Cascade"), self)
        self.cascadeAct.setStatusTip(self.tr("Cascade the windows"))
        self.connect(self.cascadeAct, QtCore.SIGNAL("triggered()"),
                     self.workspace.cascade)

        self.arrangeAct = QtGui.QAction(self.tr("Arrange &icons"), self)
        self.arrangeAct.setStatusTip(self.tr("Arrange the icons"))
        self.connect(self.arrangeAct, QtCore.SIGNAL("triggered()"),
                     self.workspace.arrangeIcons)

        self.nextAct = QtGui.QAction(self.tr("Ne&xt"), self)
        self.nextAct.setShortcut(self.tr("Ctrl+F6"))
        self.nextAct.setStatusTip(self.tr("Move the focus to the next window"))
        self.connect(self.nextAct, QtCore.SIGNAL("triggered()"),
                     self.workspace.activateNextWindow)

        self.previousAct = QtGui.QAction(self.tr("Pre&vious"), self)
        self.previousAct.setShortcut(self.tr("Ctrl+Shift+F6"))
        self.previousAct.setStatusTip(self.tr("Move the focus to the previous "
                                              "window"))
        self.connect(self.previousAct, QtCore.SIGNAL("triggered()"),
                     self.workspace.activatePreviousWindow)

        self.separatorAct = QtGui.QAction(self)
        self.separatorAct.setSeparator(True)

        self.aboutAct = QtGui.QAction(self.tr("&About"), self)
        self.aboutAct.setStatusTip(self.tr("Show the application's About box"))
        self.connect(self.aboutAct, QtCore.SIGNAL("triggered()"), self.about)

        self.aboutQtAct = QtGui.QAction(self.tr("About &Qt"), self)
        self.aboutQtAct.setStatusTip(self.tr("Show the Qt library's About box"))
        self.connect(self.aboutQtAct, QtCore.SIGNAL("triggered()"),
                     QtGui.qApp, QtCore.SLOT("aboutQt()"))


        self.getStateAct = QtGui.QAction(QtGui.QIcon(":/icons/state.svg"),
                                         self.tr("Get state"), self)
        self.getStateAct.setCheckable(True)
        self.getStateAct.setStatusTip(self.tr("Retrieve State from GPIB device"))
        self.connect(self.getStateAct, QtCore.SIGNAL("triggered()"),
                     self.getState)
        self.getTraceAct = QtGui.QAction(QtGui.QIcon(":/icons/trace.svg"),
                                         self.tr("Get trace"), self)
        self.getTraceAct.setCheckable(True)
        self.getTraceAct.setStatusTip(self.tr("Retrieve Trace from GPIB device"))
        self.connect(self.getTraceAct, QtCore.SIGNAL("triggered()"),
                     self.getTrace)
        self.getCoordAct = QtGui.QAction(QtGui.QIcon(":/icons/coord.svg"),
                                         self.tr("Get coord"), self)
        self.getCoordAct.setCheckable(True)
        self.getCoordAct.setStatusTip(self.tr("Retrieve Coord from GPIB device"))
        self.connect(self.getCoordAct, QtCore.SIGNAL("triggered()"),
                     self.getCoord)

        self.channelActGroups = QtGui.QActionGroup(self)
        self.channelActGroups.setExclusive(True)
        self.channelAAct = QtGui.QAction(QtGui.QIcon(":/icons/displayA.svg"),
                                         self.tr("Channel A"), self)
        self.channelAAct.setCheckable(True)
        self.channelAAct.setChecked(True)
        self.channelAAct.setStatusTip(self.tr("Retrieve from channel A"))
        self.channelActGroups.addAction(self.channelAAct)
        self.channelBAct = QtGui.QAction(QtGui.QIcon(":/icons/displayB.svg"),
                                         self.tr("Channel B"), self)
        self.channelBAct.setCheckable(True)
        self.channelBAct.setStatusTip(self.tr("Retrieve from channel B"))
        self.channelActGroups.addAction(self.channelBAct)
        
        
    def setupUi(self):
        self.connect(self.actionPreferences, SIGNAL('triggered(bool)'),
                     self.preferencesTriggered)
        self.connect(self.actionQuit, SIGNAL('triggered(bool)'),
                     self.quitTriggered)
        self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q'))
        self.connect(self.actionOpen, SIGNAL('triggered(bool)'),
                     self.openTriggered)
        self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O'))
        self.connect(self.actionSave, SIGNAL('triggered(bool)'),
                     self.saveTriggered)
        self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S'))
        self.connect(self.actionSaveAs, SIGNAL('triggered(bool)'),
                     self.saveAsTriggered)

        
    def about(self):
        QtGui.QMessageBox.about(self, self.tr("About Q3562"),
            self.tr("A simple application to talk to the HP3562A DSA."))
          
    def updateMenus(self):
        hasMdiChild = (self.activeMdiChild() is not None)
        self.saveAsAct.setEnabled(hasMdiChild)
        self.closeAct.setEnabled(hasMdiChild)
        self.closeAllAct.setEnabled(hasMdiChild)
        self.tileAct.setEnabled(hasMdiChild)
        self.cascadeAct.setEnabled(hasMdiChild)
        self.arrangeAct.setEnabled(hasMdiChild)
        self.nextAct.setEnabled(hasMdiChild)
        self.previousAct.setEnabled(hasMdiChild)
        self.separatorAct.setVisible(hasMdiChild)
                
    def updateWindowMenu(self):
        self.windowMenu.clear()
        self.windowMenu.addAction(self.closeAct)
        self.windowMenu.addAction(self.closeAllAct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction(self.tileAct)
        self.windowMenu.addAction(self.cascadeAct)
        self.windowMenu.addAction(self.arrangeAct)
        self.windowMenu.addSeparator()
        self.windowMenu.addAction(self.nextAct)
        self.windowMenu.addAction(self.previousAct)
        self.windowMenu.addAction(self.separatorAct)
        
        windows = self.workspace.windowList()
        self.separatorAct.setVisible(len(windows) != 0)
        
        i = 0
        for child in windows:
            if i < 9:
                text = self.tr("&%1 %2").arg(i + 1).arg(child.userFriendlyName())
            else:
                text = self.tr("%1 %2").arg(i + 1).arg(child.userFriendlyName())
            i += 1
            action = self.windowMenu.addAction(text)
            action.setCheckable(True)
            action.setChecked(child == self.activeMdiChild())
            self.connect(action, QtCore.SIGNAL("triggered()"),
                         self.windowMapper, QtCore.SLOT("map()"))
            self.windowMapper.setMapping(action, child)
                
    def createMenus(self):
        self.fileMenu = self.menuBar().addMenu(self.tr("&File"))
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.saveAsAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.deviceMenu = self.menuBar().addMenu(self.tr("&Device"))
        self.deviceMenu.addAction(self.channelAAct)
        self.deviceMenu.addAction(self.channelBAct)
        self.deviceMenu.addSeparator()
        self.deviceMenu.addAction(self.getStateAct)
        self.deviceMenu.addAction(self.getTraceAct)
        self.deviceMenu.addAction(self.getCoordAct)

        self.windowMenu = self.menuBar().addMenu(self.tr("&Window"))
        self.connect(self.windowMenu, QtCore.SIGNAL("aboutToShow()"), 
                     self.updateWindowMenu)
        
        self.menuBar().addSeparator()
        self.helpMenu = self.menuBar().addMenu(self.tr("&Help"))
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)
        
    def createToolBars(self):
        self.fileToolBar = self.addToolBar(self.tr("File"))
        self.fileToolBar.setObjectName('filetoolbar')
        self.fileToolBar.addAction(self.openAct)
        self.fileToolBar.addAction(self.saveAsAct)
        self.fileToolBar.addSeparator()

        self.deviceToolBar = self.addToolBar(self.tr("Device"))
        self.deviceToolBar.setObjectName('devicetoolbar')
        self.deviceToolBar.addAction(self.channelAAct)
        self.deviceToolBar.addAction(self.channelBAct)
        self.deviceToolBar.addSeparator()
        self.deviceToolBar.addAction(self.getStateAct)
        self.deviceToolBar.addAction(self.getTraceAct)
        self.deviceToolBar.addAction(self.getCoordAct)
                
    def createStatusBar(self):
        self.statusBar().showMessage(self.tr("Ready"))
                    
    def preferencesTriggered(self, checked=False):
        PreferencesEditor(self._prefs, self).exec_()
        self.readPreferences()
        self.replotCurrent()

    def closeEvent(self, event):
        if 1:
        #if self.promptForSave():
            self._prefs._pos = self.pos()
            self._prefs._size = self.size()
            self._prefs._appState = self.saveState()
            event.accept()
        else:
            event.ignore()
        
    def open(self, checked=False):
        filenames = QtGui.QFileDialog.getOpenFileNames(self, "Open a dumped data block file", '.', 'bin files (*.bin)\nAll files (*)')
        self.openFiles(filenames)
                
    def openFiles(self, filenames):
        ok = False
        for filename in filenames:
            filename = str(filename)
            if os.path.exists(filename):
                data = open(filename).read()
                name = os.path.basename(filename)
                #name = os.path.splitext(name)[0]
                child_cls = getChild(data)
                assert child_cls
                child = child_cls(data, name=name)
                self.workspace.addWindow(child)
                child.show()
                ok = True
        self.statusBar().showMessage(self.tr("File(s) loaded"), 2000)
        return ok
                        
    def saveAs(self, checked=False):
        return
#         index = self.plotsView.selectionModel().currentIndex()
#         if index.isValid():
#             filename = QtGui.QFileDialog.getSaveFileName(self, "Selecte a file name to save HPGL file", '.', 'HPGL files (*.plt)\nAll files (*)')
#             n = index.row()
#             value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString())
#             open(filename, 'w').write(self._plots[value])
            
    def activeMdiChild(self):
        return self.workspace.activeWindow()
        
    def initializeGPIB(self):
        try:
            self.gpib_dumper = HP3562dumper(device=self._prefs.device,
                                            address=self._prefs.address,
                                            )
            self.captureThread = GPIBReceiver(self.gpib_dumper)
            self.connect(self.captureThread, SIGNAL('datablockDumped(const QByteArray)'),
                         self.datablockReceived)
            self.captureThread.start()
        except Exception, e:
            self.gpib_plotter = None

    def datablockReceived(self, datablock):
        datablock = datablock.data()
        child_cls = getChild(datablock)
        assert child_cls
        child = child_cls(datablock)
        self.workspace.addWindow(child)
        child.show()
        self.getStateAct.setChecked(False)
        self.getTraceAct.setChecked(False)
        self.getCoordAct.setChecked(False)
        self.statusBar().showMessage(self.tr("Received data block"), 2000)
        self._receiving = False

    def selectChannel(self):
        if self.channelAAct.isEnabled():
            self.captureThread.sendCommand("A")
        else:
            self.captureThread.sendCommand("B")
        
    def getState(self):
        if self._receiving:
            return
        self._receiving = True
        self.statusBar().showMessage(self.tr("Waiting for a %1 data block").arg(self.tr('state')))
        self.getStateAct.setChecked(True)
        self.captureThread.startCapture(mode="state")
        
    def getTrace(self):
        if self._receiving:
            return
        self._receiving = True        
        self.statusBar().showMessage(self.tr("Waiting for a %1 data block").arg(self.tr('trace')))
        self.getTraceAct.setChecked(True)
        self.selectChannel()
        self.captureThread.startCapture(mode="trace")

    def getCoord(self):
        if self._receiving:
            return
        self._receiving = True        
        self.statusBar().showMessage(self.tr("Waiting for a %1 data block").arg(self.tr('coord')))
        self.getCoordAct.setChecked(True)
        self.selectChannel()
        self.captureThread.startCapture(mode="coord")

class GPIBConnection(QtCore.QThread):
    def __init__(self, cnx):
        QtCore.QThread.__init__(self)
        self.gpibdumper = cnx
        
        self._cancelmutex = QtCore.QMutex()
        self._cancel = False
        self._modemutex = QtCore.QMutex()
        self._mode = None
        #self._nreceived = 0
        self._startstopmutex = QtCore.QMutex()
        self._startstop = QtCore.QWaitCondition()
        self._capturing = False
        
    def cancel(self):
        self._cancelmutex.lock()
        self._cancel = True
        self._cancelmutex.unlock()

    def sendCommand(self, cmd):
        if self._capturing:
            return
        return self.gpibdumper.send_command(cmd)
        
    def startCapture(self, mode):
        self._modemutex.lock()
        self._mode = mode
        self._modemutex.unlock()        
        self._startstop.wakeOne()

    def stopCapture(self):
        self._startstopmutex.lock()
        self._capturing = False
        self._startstopmutex.unlock()

    def run(self):
        while 1:
            self._cancelmutex.lock()
            if self._cancel:
                return
            self._cancelmutex.unlock()
            self._startstopmutex.lock()
            if not self._capturing:
                self._startstop.wait(self._startstopmutex)
                self._capturing = True
            self._startstopmutex.unlock()
            self._modemutex.lock()
            mode = self._mode
            self._modemutex.unlock()
            datablock = self.gpibdumper.dump(mode=mode)
            self._capturing = False
            timestamp = time.time()
            if datablock:
                self.emit(SIGNAL('datablockDumped(const QByteArray)'),
                          QtCore.QByteArray(datablock))
            self.msleep(10)
    
def main():
    import optparse
    opt = optparse.OptionParser('A simple PyQt4 HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)')
    opt.add_option('-m', '--mockup', default=False,
                   action="store_true",
                   dest='mockup',
                   help='Use a pseudo GPIB connection (for test purpose)',
                   )
    opt.add_option('-v', '--verbose', default=False,
                   action="store_true",
                   dest="verbose",
                   help="Verbose mode",)
    
    options, argv = opt.parse_args(sys.argv)

    a = QtGui.QApplication(argv)
    w = Qt3562()
    files = [f for f in argv[1:] if os.path.isfile(f)]
    files.sort(cmp=str_num_cmp)
    w.openFiles(files)    

    w.show()
    a.exec_()
        
if __name__ == '__main__':
    main()

mercurial