Sun, 17 Feb 2008 22:59:59 +0100
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()