# HG changeset patch # User David Douard # Date 1526393356 -7200 # Node ID 61074a32808aaac0beca58c747c5a90f376e9fe1 # Parent 2612a4d53bf8dd559ce55af4d477adbf43696a18 [HP3562A] port HP3562A app to Py3k/Qt5 diff -r 2612a4d53bf8 -r 61074a32808a pygpibtoolkit/HP3562A/datablockwidget.py --- a/pygpibtoolkit/HP3562A/datablockwidget.py Tue May 15 16:08:33 2018 +0200 +++ b/pygpibtoolkit/HP3562A/datablockwidget.py Tue May 15 16:09:16 2018 +0200 @@ -10,23 +10,27 @@ # 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., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE). -http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr +""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE). +http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org """ import re import numpy -from itertools import izip, count as icount + +from PyQt5 import QtCore, QtWidgets +from PyQt5.QtCore import Qt + -from PyQt4 import QtCore, QtGui -from PyQt4.QtCore import Qt, SIGNAL -import state_decoder -import trace_decoder -import coord_decoder from pygpibtoolkit.qt4.mpl import QMplCanvas, FigureCanvas + +from pygpibtoolkit.HP3562A import state_decoder +from pygpibtoolkit.HP3562A import trace_decoder +from pygpibtoolkit.HP3562A import coord_decoder from pygpibtoolkit.HP3562A import mathtools -children = [ ] + +children = [] + def getChild(datablock): """ @@ -37,20 +41,22 @@ return child return None -class DatablockMDIChild(QtGui.QMainWindow): + +class DatablockMDIChild(QtWidgets.QMainWindow): seqnumber = 1 _username = "Window" - + @classmethod def isValidDatablock(cls, datablock): return False - + def __init__(self, datablock, name=None): - QtGui.QMainWindow.__init__(self) + super().__init__() if name is not None: self.username = name else: - self.username = self.__class__._username + " " + str(self.seqnumber) + self.username = "{} {}".format( + self.__class__._username, self.seqnumber) self.setAttribute(Qt.WA_DeleteOnClose) self.isUntitled = True self.dataIsModified = False @@ -61,47 +67,50 @@ def setDatablock(self, datablock): self.datablock = datablock - + def setupUI(self): # setup headers views as a docked window assert isinstance(self._header_struct, tuple) self.headerDocks = [] self.tables = [] for i, header_struct in enumerate(self._header_struct): - dock = QtGui.QDockWidget("Header" + (i>0 and (' %s'%(i+1)) or ''), - self) - #dock.setFeatures(dock.NoDockWidgetFeatures) - sarea = QtGui.QScrollArea(dock) + dock = QtWidgets.QDockWidget( + "Header" + (i > 0 and (' %s' % (i+1)) or ''), + self) + # dock.setFeatures(dock.NoDockWidgetFeatures) + sarea = QtWidgets.QScrollArea(dock) dock.setWidget(sarea) self.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) self.headerDocks.append(dock) - l = QtGui.QVBoxLayout(sarea) - l.setContentsMargins(0,0,0,0) - table = QtGui.QTableWidget(sarea) - table.setStyleSheet('font-size: 10px;') - self.setupRowsHeight(table) + l = QtWidgets.QVBoxLayout(sarea) + l.setContentsMargins(0, 0, 0, 0) + table = QtWidgets.QTableWidget(sarea) + # table.setStyleSheet('font-size: 10px;') + # self.setupRowsHeight(table) table.setShowGrid(False) table.setAlternatingRowColors(True) table.verticalHeader().hide() l.addWidget(table, 1) self.tables.append(table) - + def setupRowsHeight(self, table): - if table.verticalHeader().minimumSectionSize()>0: + if table.verticalHeader().minimumSectionSize() > 0: cellsize = table.verticalHeader().minimumSectionSize() else: cellsize = 15 table.verticalHeader().setDefaultSectionSize(cellsize) def updateHeaderData(self): - for header, table, header_struct in izip(self.header, self.tables, self._header_struct): + for header, table, header_struct in zip( + self.header, self.tables, self._header_struct): table.clear() table.setRowCount(len(header_struct)) table.setColumnCount(2) table.setHorizontalHeaderLabels(['Parameter', 'Value']) - bool_re = re.compile(r'((?P.*) )?(?P\w+/\w+)( (?P.*))?') - item = QtGui.QTableWidgetItem() + bool_re = re.compile( + r'((?P.*) )?(?P\w+/\w+)( (?P.*))?') + item = QtWidgets.QTableWidgetItem() item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) for i, row in enumerate(header_struct): pname = row[0] @@ -110,9 +119,7 @@ if typ is None: continue val = header.get(key, "N/A") - if isinstance(val, basestring): - val = unicode(val) - elif typ is bool and isinstance(val, typ): + if typ is bool and isinstance(val, typ): m = bool_re.match(key) if m: d = m.groupdict() @@ -125,29 +132,29 @@ val = d['flag'].split('/')[not val] else: val = str(val) - else: + else: val = str(val) val = val.strip() if val: - if val[0]+val[-1] in ['""',"''"]: + if val[0]+val[-1] in ['""', "''"]: val = val[1:-1] - if val[0:2]+val[-1] in ['u""',"u''"]: + if val[0:2]+val[-1] in ['u""', "u''"]: val = val[2:-1] while val and val.endswith(chr(0)): val = val[:-1] - item_ = QtGui.QTableWidgetItem(item) + item_ = QtWidgets.QTableWidgetItem(item) item_.setText(key) table.setItem(i, 0, item_) - item_ = QtGui.QTableWidgetItem(item) + item_ = QtWidgets.QTableWidgetItem(item) item_.setText(val) table.setItem(i, 1, item_) table.resizeColumnsToContents() - #table.resizeRowsToContents() - #self.setupRowsHeight(self.table) - + # table.resizeRowsToContents() + # self.setupRowsHeight(self.table) + def userFriendlyName(self): return self.username - + def closeEvent(self, event): if self.maybeSave(): event.accept() @@ -156,90 +163,90 @@ def maybeSave(self): if self.dataIsModified: - ret = QtGui.QMessageBox.warning(self, self.tr("MDI"), - self.tr("'%1' has been modified.\n"\ - "Do you want to save your changes?") - .arg(self.userFriendlyCurrentFile()), - QtGui.QMessageBox.Yes | QtGui.QMessageBox.Default, - QtGui.QMessageBox.No, - QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Escape) - if ret == QtGui.QMessageBox.Yes: + ret = QtWidgets.QMessageBox.warning( + self, self.tr("MDI"), + self.tr("'%1' has been modified.\n" + "Do you want to save your changes?") + .arg(self.userFriendlyCurrentFile()), + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Default, + QtWidgets.QMessageBox.No, + QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Escape) + if ret == QtWidgets.QMessageBox.Yes: return self.save() - elif ret == QtGui.QMessageBox.Cancel: + elif ret == QtWidgets.QMessageBox.Cancel: return False return True + class StateBinaryDatablockMDIChild(DatablockMDIChild): _username = "State" - _header_struct = state_decoder.HEADER, + _header_struct = (state_decoder.HEADER, ) + @classmethod def isValidDatablock(cls, datablock): try: h = state_decoder.decode_state(datablock) - assert len(h)>0 + assert len(h) > 0 return True - except Exception, e: + except Exception as e: return False def setDatablock(self, datablock): - DatablockMDIChild.setDatablock(self, datablock) + super().setDatablock(datablock) self.header = [state_decoder.decode_state(self.datablock)] + class TraceBinaryDatablockMDIChild(DatablockMDIChild): _username = "Trace" - _header_struct = trace_decoder.HEADER, + _header_struct = (trace_decoder.HEADER, ) + @classmethod def isValidDatablock(cls, datablock): try: h, t = trace_decoder.decode_trace(datablock) - assert len(h)>0 + assert len(h) > 0 assert len(t) return True - except Exception, e: + except Exception as e: return False def __init__(self, datablock, name=None): - DatablockMDIChild.__init__(self, datablock, name) + super().__init__(datablock, name) self.updateTraceData() - + def setDatablock(self, datablock): - DatablockMDIChild.setDatablock(self, datablock) + super().setDatablock(datablock) self.header, self.trace = trace_decoder.decode_trace(self.datablock) self.header = [self.header] - + def setupToolBar(self): - toolbar = QtGui.QToolBar(self) + toolbar = QtWidgets.QToolBar(self) self.addToolBar(toolbar) - self.ylogaction = QtGui.QAction(self.tr("Y Log"), self) + self.ylogaction = QtWidgets.QAction(self.tr("Y Log"), self) self.ylogaction.setCheckable(True) - self.connect(self.ylogaction, SIGNAL('toggled(bool)'), - self.updateTraceData) + self.ylogaction.toggled.connect(self.updateTraceData) toolbar.addAction(self.ylogaction) - - + def setupUI(self): self.setupToolBar() - DatablockMDIChild.setupUI(self) - mainw = QtGui.QWidget(self) - l = QtGui.QVBoxLayout(mainw) - l.setMargin(0) self.canvas = QMplCanvas(self) + super().setupUI() + mainw = QtWidgets.QWidget(self) + l = QtWidgets.QVBoxLayout(mainw) + l.setContentsMargins(0, 0, 0, 0) l.addWidget(self.canvas, 1) - + self.setCentralWidget(mainw) - + def updateTraceData(self): - #self.canvas.deleteLater() - #self.canvas = QMplCanvas() - #self.centralWidget().layout().addWidget(self.canvas, 1) f0 = self.header[0]['Start freq value'] dx = self.header[0]['Delta X-axis'] n = self.header[0]['Number of elements'] - x = numpy.linspace(f0, f0+dx*n, len(self.trace)) + x = numpy.linspace(f0, f0+dx*n, len(self.trace)) y = self.trace.copy() if self.ylogaction.isChecked(): - minv = min(y[y>0]) - y[y==0] = minv + minv = min(y[y > 0]) + y[y == 0] = minv y = numpy.log10(y) y = y*10 self.canvas.axes.clear() @@ -255,49 +262,52 @@ if self.header[0]['Display function']: self.canvas.axes.set_title(self.header[0]['Display function']) + # compute THD, if any y = self.trace.copy() if f0 > 0: # must add some initial zeros - yy = numpy.zeros(f0/dx+len(y)) + yy = numpy.zeros(int(f0/dx) + len(y)) yy[-len(y):] = y y = yy msg = "" try: f0, thd = mathtools.thd(y, db=True) - f0 = f0*dx + f0 = f0 * dx assert thd except: pass else: - msg += 'THD:%.2g db Freq:%.2f Hz '%(thd, f0) + msg += 'THD:%.2g db Freq:%.2f Hz ' % (thd, f0) try: thdn = mathtools.thd_n(y, db=True) except: pass else: - msg += 'THD+N:%.2g db '%thdn + msg += 'THD+N:%.2g db ' % thdn self.statusBar().showMessage(msg) self.canvas.draw() class CoordBinaryDatablockMDIChild(TraceBinaryDatablockMDIChild): _username = "Coord" - _header_struct = coord_decoder.TRACE_HEADER, coord_decoder.HEADER, + _header_struct = (coord_decoder.TRACE_HEADER, coord_decoder.HEADER, ) + @classmethod def isValidDatablock(cls, datablock): try: h1, h2, t = coord_decoder.decode_coord(datablock) - assert len(h1)>0 - assert len(h2)>0 - assert len(t)>0 + assert len(h1) > 0 + assert len(h2) > 0 + assert len(t) > 0 return True - except Exception, e: + except Exception as e: return False + def setupToolBar(self): pass def setDatablock(self, datablock): - DatablockMDIChild.setDatablock(self, datablock) + super().setDatablock(datablock) h1, h2, self.trace = coord_decoder.decode_coord(self.datablock) self.header = [h2, h1] @@ -305,7 +315,7 @@ f0 = self.header[0]['Start freq value'] dx = self.header[0]['Delta X-axis'] n = self.header[0]['Number of elements'] - x = numpy.linspace(f0, f0+dx*n, len(self.trace)) + x = numpy.linspace(f0, f0+dx*n, len(self.trace)) y = self.trace.copy() y = y.clip(min=self.header[1]['Min value of data'], @@ -330,6 +340,3 @@ children.append(CoordBinaryDatablockMDIChild) children.append(TraceBinaryDatablockMDIChild) children.append(StateBinaryDatablockMDIChild) - - - diff -r 2612a4d53bf8 -r 61074a32808a pygpibtoolkit/HP3562A/q3562A.py --- a/pygpibtoolkit/HP3562A/q3562A.py Tue May 15 16:08:33 2018 +0200 +++ b/pygpibtoolkit/HP3562A/q3562A.py Tue May 15 16:09:16 2018 +0200 @@ -11,35 +11,34 @@ # 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., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -""" Copyright (c) 2007-2008 David Douard (Paris, FRANCE). -http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@logilab.fr +""" Copyright (c) 2007-2018 David Douard (Paris, FRANCE). +http://www.logilab.org/project/pygpibtoolkit -- mailto:david.douard@sdfa3.org """ -import os, sys -import time -import glob +import os +import sys -from PyQt4 import QtGui, QtCore, uic -from PyQt4.QtCore import SIGNAL, Qt +from PyQt5 import QtWidgets, QtGui, QtCore +from PyQt5.QtCore import pyqtSignal -# WARNING, may be "replaced" by mockup -from dump_datablock import HP3562dumper +from pygpibtoolkit.tools import str_num_key -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.qt5.qpreferences import ( + BaseItem, IntItem, UnicodeItem, ColorItem, + PointItem, SizeItem, ByteArrayItem, AbstractPreferences) +from pygpibtoolkit.qt5.qpreferenceseditor import PreferencesEditor +import pygpibtoolkit.qt5.resources_rc +import pygpibtoolkit.qt5.icons_ressource_rc -from pygpibtoolkit.tools import str_num_cmp +# WARNING, may be "replaced" by mock +from pygpibtoolkit.HP3562A.dump_datablock import HP3562dumper -import pygpibtoolkit.qt4.resources_rc -import pygpibtoolkit.qt4.icons_ressource_rc +from pygpibtoolkit.HP3562A.datablockwidget import getChild -from datablockwidget import getChild class Preferences(AbstractPreferences): - ORGANISATION="Logilab" - APPLICATION="q3562" + ORGANISATION = "Logilab" + APPLICATION = "q3562" _pos = PointItem() _size = SizeItem() @@ -52,24 +51,23 @@ 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): + + +class Qt3562(QtWidgets.QMainWindow): def __init__(self, parent=None): - QtGui.QMainWindow.__init__(self, parent) - self.workspace = QtGui.QWorkspace() + super().__init__(parent) + self.workspace = QtWidgets.QMdiArea(self) self.setCentralWidget(self.workspace) - self.connect(self.workspace, SIGNAL("windowActivated(QWidget *)"), - self.updateMenus) + self.workspace.subWindowActivated.connect(self.updateMenus) self.windowMapper = QtCore.QSignalMapper(self) - self.connect(self.windowMapper, QtCore.SIGNAL("mapped(QWidget *)"), - self.workspace, QtCore.SLOT("setActiveWindow(QWidget *)")) - + self.windowMapper.mapped.connect(self.workspace.setActiveSubWindow) + self._receiving = False - + self.createActions() self.createMenus() self.createToolBars() @@ -88,131 +86,130 @@ 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 - + # 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.openAct = QtWidgets.QAction( + QtGui.QIcon(":/icons/open.png"), + self.tr("&Open..."), self, + shortcut=self.tr("Ctrl+O"), + statusTip=self.tr("Open an existing data block file"), + triggered=self.open) - 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.saveAsAct = QtWidgets.QAction( + self.tr("Save &As..."), + self, statusTip=self.tr("Save the document under a new name"), + triggered=self.saveAs) - 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.exitAct = QtWidgets.QAction( + self.tr("E&xit"), self, + shortcut=self.tr("Ctrl+Q"), + statusTip=self.tr("Exit the application"), + triggered=self.close) + + self.closeAct = QtWidgets.QAction( + self.tr("Cl&ose"), self, + shortcut=self.tr("Ctrl+W"), + statusTip=self.tr("Close the active window"), + triggered=self.workspace.closeActiveSubWindow) - 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.closeAllAct = QtWidgets.QAction( + self.tr("Close &All"), self, + statusTip=self.tr("Close all the windows"), + triggered=self.workspace.closeAllSubWindows) - 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.tileAct = QtWidgets.QAction( + self.tr("&Tile"), self, + statusTip=self.tr("Tile the windows"), + triggered=self.workspace.tileSubWindows) - 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.cascadeAct = QtWidgets.QAction( + self.tr("&Cascade"), self, + statusTip=self.tr("Cascade the windows"), + triggered=self.workspace.cascadeSubWindows) - 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.arrangeAct = QtWidgets.QAction( + # self.tr("Arrange &icons"), self, + # statusTip=self.tr("Arrange the icons"), + # triggered=self.workspace.arrangeIcons) - 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.nextAct = QtWidgets.QAction( + self.tr("Ne&xt"), self, + shortcut=self.tr("Ctrl+F6"), + statusTip=self.tr("Move the focus to the next window"), + triggered=self.workspace.activateNextSubWindow) - self.separatorAct = QtGui.QAction(self) + self.previousAct = QtWidgets.QAction( + self.tr("Pre&vious"), self, + shortcut=self.tr("Ctrl+Shift+F6"), + statusTip=self.tr("Move the focus to the previous window"), + triggered=self.workspace.activatePreviousSubWindow) + + self.separatorAct = QtWidgets.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.aboutAct = QtWidgets.QAction( + self.tr("&About"), self, + statusTip=self.tr("Show the application's About box"), + 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.aboutQtAct = QtWidgets.QAction( + self.tr("About &Qt"), self, + statusTip=self.tr("Show the Qt library's About box"), + triggered=QtWidgets.qApp.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.getStateAct = QtWidgets.QAction( + QtGui.QIcon(":/icons/state.svg"), + self.tr("Get state"), self, + checkable=True, + statusTip=self.tr("Retrieve State from GPIB device"), + triggered=self.getState) + + self.getTraceAct = QtWidgets.QAction( + QtGui.QIcon(":/icons/trace.svg"), + self.tr("Get trace"), self, + checkable=True, + statusTip=self.tr("Retrieve Trace from GPIB device"), + triggered=self.getTrace) + + self.getCoordAct = QtWidgets.QAction( + QtGui.QIcon(":/icons/coord.svg"), + self.tr("Get coord"), self, + checkable=True, + statusTip=self.tr("Retrieve Coord from GPIB device"), + 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 = QtWidgets.QActionGroup(self, exclusive=True) + self.channelAAct = QtWidgets.QAction( + QtGui.QIcon(":/icons/displayA.svg"), + self.tr("Channel A"), self, + checkable=True, checked=True, + statusTip=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.channelBAct = QtWidgets.QAction( + QtGui.QIcon(":/icons/displayB.svg"), + self.tr("Channel B"), self, + checkable=True, checked=False, + statusTip=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.actionPreferences.triggered.connect(self.preferencesTriggered) + self.actionQuit.triggered.connect(self.quitTriggered) self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q')) - self.connect(self.actionOpen, SIGNAL('triggered(bool)'), - self.openTriggered) + self.actionOpen.triggered.connect(self.openTriggered) self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O')) - self.connect(self.actionSave, SIGNAL('triggered(bool)'), - self.saveTriggered) + self.actionSave.triggered.connect(self.saveTriggered) self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S')) - self.connect(self.actionSaveAs, SIGNAL('triggered(bool)'), - self.saveAsTriggered) + self.actionSaveAs.triggered.connect(self.saveAsTriggered) - def about(self): - QtGui.QMessageBox.about(self, self.tr("About Q3562"), + QtWidgets.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) @@ -220,11 +217,11 @@ self.closeAllAct.setEnabled(hasMdiChild) self.tileAct.setEnabled(hasMdiChild) self.cascadeAct.setEnabled(hasMdiChild) - self.arrangeAct.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) @@ -232,29 +229,30 @@ self.windowMenu.addSeparator() self.windowMenu.addAction(self.tileAct) self.windowMenu.addAction(self.cascadeAct) - self.windowMenu.addAction(self.arrangeAct) + # 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() + + windows = self.workspace.subWindowList() 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()) + text = self.tr("&{0} {1}").format( + i + 1, child.userFriendlyName()) else: - text = self.tr("%1 %2").arg(i + 1).arg(child.userFriendlyName()) + text = self.tr("%{0} {1}").format( + i + 1, 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()")) + action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, child) - + def createMenus(self): self.fileMenu = self.menuBar().addMenu(self.tr("&File")) self.fileMenu.addAction(self.openAct) @@ -271,14 +269,13 @@ self.deviceMenu.addAction(self.getCoordAct) self.windowMenu = self.menuBar().addMenu(self.tr("&Window")) - self.connect(self.windowMenu, QtCore.SIGNAL("aboutToShow()"), - self.updateWindowMenu) - + self.windowMenu.aboutToShow.connect(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') @@ -294,10 +291,10 @@ 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() @@ -312,28 +309,31 @@ 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) - + filenames = QtWidgets.QFileDialog.getOpenFileNames( + self, "Open a dumped data block file", '.', + 'bin files (*.bin)\nAll files (*)') + self.openFiles(filenames[0]) + def openFiles(self, filenames): ok = False for filename in filenames: filename = str(filename) if os.path.exists(filename): - data = open(filename).read() + with open(filename, 'rb') as f: + data = f.read() name = os.path.basename(filename) - #name = os.path.splitext(name)[0] + # name = os.path.splitext(name)[0] child_cls = getChild(data) assert child_cls child = child_cls(data, name=name) - self.workspace.addWindow(child) + self.workspace.addSubWindow(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() @@ -342,30 +342,29 @@ # 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() - + return self.workspace.activeSubWindow() + def initializeGPIB(self): try: self.gpib_dumper = HP3562dumper(device=self._prefs.device, address=self._prefs.address, ) self.captureThread = GPIBConnection(self.gpib_dumper) - self.connect(self.captureThread, SIGNAL('datablockDumped(const QByteArray)'), - self.datablockReceived) + self.captureThread.datablockDumped.connect(self.datablockReceived) self.captureThread.start() - except Exception, e: - print "ERROR: cannot initialize GPIB" - print e + except Exception as e: + print("ERROR: cannot initialize GPIB") + print(e) self.gpib_plotter = None def datablockReceived(self, datablock): - datablock = datablock.data() + # datablock = datablock.data() child_cls = getChild(datablock) assert child_cls child = child_cls(datablock) - self.workspace.addWindow(child) + self.workspace.addSubWindow(child) child.show() self.getStateAct.setChecked(False) self.getTraceAct.setChecked(False) @@ -374,53 +373,62 @@ self._receiving = False # give control back to fron panel self.captureThread.sendCommand('LCL') - + 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.statusBar().showMessage( + self.tr("Waiting for a %s data block") % (self.tr('state'))) self.getStateAct.setChecked(True) - self.captureThread.startCapture(mode="state") - + if self.captureThread: + 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._receiving = True + self.statusBar().showMessage( + self.tr("Waiting for a %s data block") % (self.tr('trace'))) self.getTraceAct.setChecked(True) self.selectChannel() - self.captureThread.startCapture(mode="trace") + if self.captureThread: + 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._receiving = True + self.statusBar().showMessage( + self.tr("Waiting for a %s data block") % (self.tr('coord'))) self.getCoordAct.setChecked(True) self.selectChannel() - self.captureThread.startCapture(mode="coord") + if self.captureThread: + self.captureThread.startCapture(mode="coord") + class GPIBConnection(QtCore.QThread): + datablockDumped = pyqtSignal(object) + 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._nreceived = 0 self._startstopmutex = QtCore.QMutex() self._startstop = QtCore.QWaitCondition() self._capturing = False - + def cancel(self): self._cancelmutex.lock() self._cancel = True @@ -430,11 +438,11 @@ if self._capturing: return return self.gpibdumper.send_command(cmd) - + def startCapture(self, mode): self._modemutex.lock() self._mode = mode - self._modemutex.unlock() + self._modemutex.unlock() self._startstop.wakeOne() def stopCapture(self): @@ -458,15 +466,15 @@ 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.datablockDumped.emit(datablock) self.msleep(10) - + + def main(): import optparse - opt = optparse.OptionParser('A simple PyQt4 HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)') + opt = optparse.OptionParser( + 'A simple GPIB-based nterface for the HP3562A DSA') opt.add_option('-m', '--mockup', default=False, action="store_true", dest='mockup', @@ -476,22 +484,22 @@ action="store_true", dest="verbose", help="Verbose mode",) - + options, argv = opt.parse_args(sys.argv) if options.mockup: # overwrite "normal" module global HP3562dumper - from dump_datablock_mockup import HP3562dumper + from pygpibtoolkit.HP3562A.dump_datablock_mockup import HP3562dumper - a = QtGui.QApplication(argv) + a = QtWidgets.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) + files.sort(key=str_num_key) + w.openFiles(files) w.show() a.exec_() - + if __name__ == '__main__': main() diff -r 2612a4d53bf8 -r 61074a32808a pygpibtoolkit/tools.py --- a/pygpibtoolkit/tools.py Tue May 15 16:08:33 2018 +0200 +++ b/pygpibtoolkit/tools.py Tue May 15 16:09:16 2018 +0200 @@ -22,17 +22,12 @@ return (a > b) - (a < b) -def str_num_cmp(s1, s2): - """ - string comparator function that will put 'toto_10' after 'toto_2' +def str_num_key(s1): + """string comparator function that will allow to put 'toto_10' after 'toto_2' Also works for strings like 'toto_1_et_23er25'. """ r = re.compile(r'((?<=\d)\D+|(?<=\D)\d+)') - r1 = r.split(s1) - r2 = r.split(s2) - r1 = [not x.isdigit() and x or int(x) for x in r1 if x] - r2 = [not x.isdigit() and x or int(x) for x in r2 if x] - return cmp(r1, r2) + return [not x.isdigit() and x or int(x) for x in r.split(s1) if x] class AbstractRegister: diff -r 2612a4d53bf8 -r 61074a32808a setup.py --- a/setup.py Tue May 15 16:08:33 2018 +0200 +++ b/setup.py Tue May 15 16:09:16 2018 +0200 @@ -13,6 +13,7 @@ 'hp3562-state=pygpibtoolkit.HP3562A.state_decoder:main', 'hp3562-trace=pygpibtoolkit.HP3562A.trace_decoder:main', 'hp3562-dump=pygpibtoolkit.HP3562A.dump_datablock:main', + 'hp3562=pygpibtoolkit.HP3562A.q3562A:main', 'pygpib-plotter=pygpibtoolkit.plotter.qgpib_plotter:main', ]} )