Sun, 03 Jun 2018 18:04:11 +0200
[plotter] fix and improve a bit the HPGL plotter emulator
at least, the mocked version now works again
# 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., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ Copyright (c) 2007-2018 David Douard (Paris, FRANCE). https://bitbucket.org/dddouard/pygpibtoolkit -- mailto:david.douard@sdfa3.org """ import os import sys import time from PyQt5 import QtWidgets, QtGui, QtCore, QtPrintSupport, uic from PyQt5.QtCore import Qt, pyqtSignal from pygpibtoolkit.plotter.hpgl_qt import QHPGLPlotterWidget from pygpibtoolkit.plotter.gpib_plotter import GPIBplotter from pygpibtoolkit.qt5.qpreferences import IntItem, UnicodeItem, ColorItem from pygpibtoolkit.qt5.qpreferences import BoolItem from pygpibtoolkit.qt5.qpreferences import PointItem, SizeItem, ByteArrayItem from pygpibtoolkit.qt5.qpreferences import AbstractPreferences from pygpibtoolkit.qt5.qpreferenceseditor import PreferencesEditor from pygpibtoolkit.tools import str_num_key HERE = os.path.abspath(os.path.dirname(__file__)) try: form_class, base_class = uic.loadUiType( os.path.join(HERE, "qhpgl_plotter.ui"), from_imports=True, import_from='pygpibtoolkit.plotter',) except Exception as e: from pygpibtoolkit.plotter.qhpgl_plotter_ui import ( Ui_MainWindow as form_class) class Preferences(AbstractPreferences): ORGANISATION = "PyGPIBToolkit" APPLICATION = "qgpib_plotter" _pos = PointItem() _size = SizeItem() _appState = ByteArrayItem() device = UnicodeItem(default='/dev/ttyUSB0', name=u'device', description=u'GPIB device', group="GPIB settings") address = IntItem(default=5, min=0, max=16, name=u'GPIB address', group="GPIB settings") background = ColorItem(default=QtGui.QColor("white"), name="Background", group="Colors") color0 = ColorItem(default=QtGui.QColor("black"), name="Pen #0", group="Colors") color1 = ColorItem(default=QtGui.QColor("green"), name="Pen #1", group="Colors") color2 = ColorItem(default=QtGui.QColor("red"), name="Pen #2", group="Colors") color3 = ColorItem(default=QtGui.QColor("blue"), name="Pen #3", group="Colors") color4 = ColorItem(default=QtGui.QColor("yellow"), name="Pen #4", group="Colors") color5 = ColorItem(default=QtGui.QColor("cyan"), name="Pen #5", group="Colors") color6 = ColorItem(default=QtGui.QColor("magenta"), name="Pen #6", group="Colors") color7 = ColorItem(default=QtGui.QColor("darkred"), name="Pen #7", group="Colors") autodisplay = BoolItem( default=True, name="Auto display", description=( "Automatically display a new plot as soon as it is received"), group="Misc") class QtHPGLPlotter(QtWidgets.QMainWindow, form_class): def __init__(self, parent=None): super().__init__(parent) self._plots = {} self._prefs = Preferences() self._receiving = False self.setupUi(self) 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 if bg and bg.isValid(): self.plotterWidget.qview.setBackgroundBrush(QtGui.QBrush(bg)) pen_colors = [self._prefs["color%d" % i] for i in range(8)] self.plotterWidget.pen_colors = pen_colors def replotCurrent(self): self.currentPlotChanged(self.plotsView.currentIndex()) def setupUi(self, widget): super().setupUi(widget) # actions defined in designer self.actionPreferences.triggered.connect(self.preferencesTriggered) self.actionQuit.triggered.connect(self.quitTriggered) self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q')) self.actionOpen.triggered.connect(self.openTriggered) self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O')) self.actionSave.triggered.connect(self.saveTriggered) self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S')) self.actionSaveAs.triggered.connect(self.saveAsTriggered) self.actionPrint.setShortcut(QtGui.QKeySequence(u'Ctrl+P')) self.actionPrint.triggered.connect(self.printTriggered) self.plotterWidget = QHPGLPlotterWidget(self) self.setCentralWidget(self.plotterWidget) self.captureButton.toggled.connect(self.captureToggled) self._plots_list = QtCore.QStringListModel() self.plotsView.setModel(self._plots_list) self.plotsView.activated.connect(self.currentPlotChanged) self.plotsView.selectionModel().currentChanged.connect( self.currentPlotChanged) self.setReceivingLed() def currentPlotChanged(self, index, old_index=None): if index.isValid(): value = self.plotsView.model().data(index, Qt.DisplayRole) self.plotterWidget.clear() self.plotterWidget.parse(self._plots[value]) def preferencesTriggered(self, checked=False): PreferencesEditor(self._prefs, self).exec_() self.readPreferences() self.replotCurrent() def quitTriggered(self, checked=False): self.close() def closeEvent(self, event): # if self.promptForSave(): if 1: self._prefs._pos = self.pos() self._prefs._size = self.size() self._prefs._appState = self.saveState() event.accept() else: event.ignore() def openTriggered(self, checked=False): filenames = QtWidgets.QFileDialog.getOpenFileNames( self, "Open a HPGL file to display", '.', 'HPGL files (*.plt)\nAll files (*)') self.openFiles(filenames[0]) # self.displayFirst() def displayFirst(self): if not self.plotsView.currentIndex().isValid(): self.plotsView.setCurrentIndex(self.plotsView.model().index(0, 0)) def openFiles(self, filenames): ok = False for filename in filenames: if os.path.exists(filename): data = open(filename).read() name = os.path.basename(filename) name = os.path.splitext(name)[0] lst = self.plotsView.model().stringList() lst.append(name) self._plots[name] = data self.plotsView.model().setStringList(lst) ok = True if ok: self.plotsView.setCurrentIndex( self.plotsView.model().index(len(lst)-1, 0)) return ok def plotReceived(self, num): self._receiving = False self.setReceivingLed() plot, timestamp = self.captureThread.getPlot(num) name = "plot_%s" % (num) self._plots[name] = plot model = self.plotsView.model() pos = model.rowCount() model.insertRows(pos, 1) model.setData(model.index(pos), name) if self._prefs.autodisplay: self.plotsView.setCurrentIndex(model.index(pos)) def plotStarted(self): self._receiving = True self.setReceivingLed() def printTriggered(self, checked=False): cindex = self.plotsView.currentIndex() if cindex and cindex.isValid(): # isn't there a simpler way of getting this? plotname = self.plotsView.model().data(cindex, Qt.DisplayRole) printer = QtPrintSupport.QPrinter() printer.setOutputFormat(printer.PdfFormat) printer.setOutputFileName('%s.pdf' % plotname) prdialog = QtPrintSupport.QPrintDialog(printer) prdialog.setEnabledOptions(prdialog.PrintToFile) if prdialog.exec_() == prdialog.Accepted: painter = QtGui.QPainter(printer) painter.setRenderHint(painter.Antialiasing) self.plotterWidget.qview.render(painter) painter.end() else: QtWidgets.QMessageBox.warning( self, "Nothing to print", "Unable to print: <br><b>No plot is currently selected.</b>") def saveTriggered(self, checked=False): print("save") def saveAsTriggered(self, checked=False): 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 (*)') value = self.plotsView.model().data(index, Qt.DisplayRole) open(filename, 'w').write(self._plots[value]) def initializeGPIB(self): self._online = False try: self.gpib_plotter = GPIBplotter( device=self._prefs.device, address=self._prefs.address, ) self.captureThread = GPIBReceiver(self.gpib_plotter) self.captureThread.plotReceived.connect(self.plotReceived) self.captureThread.plotStarted.connect(self.plotStarted) self.captureThread.start() except Exception as e: self.captureThread = None self.gpib_plotter = None self.setCaptureLed() def captureToggled(self, state): if state: if self.gpib_plotter is None: self.initializeGPIB() if self.gpib_plotter is None: QtWidgets.QMessageBox.critical( self, "GPIB error", "<b>Unable to initialize GPIB connection</b>." "<br>Please check your GPIB dongle and settings.") self._online = False self.setCaptureLed() return self._online = True self.captureThread.startCapture() else: if self.captureThread: self.captureThread.stopCapture() self._online = False self.setCaptureLed() def setCaptureLed(self): if self._online: icn = QtGui.QIcon(':/icons/led_green.svg') else: icn = QtGui.QIcon(':/icons/led_green_off.svg') self.captureButton.setIcon(icn) self.captureButton.setChecked(self._online) def setReceivingLed(self): if self._receiving: icn = QtGui.QIcon(':/icons/led_red.svg') else: icn = QtGui.QIcon(':/icons/led_red_off.svg') self.receivingButton.setIcon(icn) class GPIBReceiver(QtCore.QThread): plotStarted = pyqtSignal() plotReceived = pyqtSignal(int) def __init__(self, cnx, parent=None): super().__init__(parent) self.gpibplotter = cnx self.gpibplotter.plot_started_cb = self.plotStarted.emit self._cancel = False # self._nreceived = 0 self._plotsmutex = QtCore.QMutex() self._plots = [] self._capturing = False def cancel(self): self._cancel = True def startCapture(self): self._capturing = True def stopCapture(self): self._capturing = False def run(self): while not self._cancel: if not self._capturing: self.msleep(100) continue plot = self.gpibplotter.load_plot(wait_timeout=0.1) timestamp = time.time() if plot: self._plotsmutex.lock() self._plots.append((plot, timestamp)) n = len(self._plots) self._plotsmutex.unlock() self.plotReceived.emit(n - 1) self.msleep(10) def getPlot(self, num): self._plotsmutex.lock() try: return self._plots[num] finally: self._plotsmutex.unlock() def main(): global GPIBplotter 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) if options.mockup: from pygpibtoolkit.plotter.gpib_plotter_mockup import GPIBplotter a = QtWidgets.QApplication(argv) w = QtHPGLPlotter() files = [f for f in argv[1:] if os.path.isfile(f)] files.sort(key=str_num_key) if w.openFiles(files): w.displayFirst() w.show() a.exec_() if __name__ == '__main__': main()