plotter/qgpib_plotter.py

changeset 40
1bbea188a7e5
parent 39
8becd52c2171
child 41
9a453b2479c5
equal deleted inserted replaced
39:8becd52c2171 40:1bbea188a7e5
1 #
2
3 import os, sys
4 import time
5 import glob
6
7 from PyQt4 import QtGui, QtCore, uic
8 from PyQt4.QtCore import SIGNAL, Qt
9
10 if "-m" in sys.argv:
11 from gpib_plotter_mockup import GPIBplotter
12 else:
13 from gpib_plotter import GPIBplotter
14 from hpgl_qt import QHPGLPlotterWidget
15 import hpgl_plotter_rc
16
17 ldir = os.path.abspath(os.path.dirname(__file__))
18 sys.path.append(ldir)
19 form_class, base_class = uic.loadUiType(os.path.join(ldir, "qhpgl_plotter.ui"))
20
21 from qpreferences import BaseItem, IntItem, UnicodeItem, ColorItem
22 from qpreferences import PointItem, SizeItem, ByteArrayItem
23 from qpreferences import AbstractPreferences
24 from qpreferenceseditor import PreferencesEditor
25
26 from tools import str_num_cmp
27
28 class Preferences(AbstractPreferences):
29 ORGANISATION="Logilab"
30 APPLICATION="qgpib_plotter"
31
32 _pos = PointItem()
33 _size = SizeItem()
34 _appState = ByteArrayItem()
35
36 device = UnicodeItem(default='/dev/ttyUSB0',
37 name=u'device',
38 description=u'GPIB device',
39 group="GPIB settings")
40 address = IntItem(default=5, min=0, max=16,
41 name=u'GPIB address',
42 group="GPIB settings")
43
44 background = ColorItem(default=QtGui.QColor("white"),
45 name="Background",
46 group="Colors")
47 color0 = ColorItem(default=QtGui.QColor("black"),
48 name="Pen #0",
49 group="Colors")
50 color1 = ColorItem(default=QtGui.QColor("green"),
51 name="Pen #1",
52 group="Colors")
53 color2 = ColorItem(default=QtGui.QColor("red"),
54 name="Pen #2",
55 group="Colors")
56 color3 = ColorItem(default=QtGui.QColor("blue"),
57 name="Pen #3",
58 group="Colors")
59 color4 = ColorItem(default=QtGui.QColor("yellow"),
60 name="Pen #4",
61 group="Colors")
62 color5 = ColorItem(default=QtGui.QColor("cyan"),
63 name="Pen #5",
64 group="Colors")
65 color6 = ColorItem(default=QtGui.QColor("magenta"),
66 name="Pen #6",
67 group="Colors")
68 color7 = ColorItem(default=QtGui.QColor("darkred"),
69 name="Pen #7",
70 group="Colors")
71
72 class QtHPGLPlotter(QtGui.QMainWindow, form_class):
73 def __init__(self, parent=None):
74 QtGui.QMainWindow.__init__(self, parent)
75 self._plots = {}
76 self._prefs = Preferences()
77 self.setupUi()
78 self.initializeGPIB()
79 if self._prefs._pos:
80 self.move(self._prefs._pos)
81 if self._prefs._size:
82 self.resize(self._prefs._size)
83 if self._prefs._appState:
84 self.restoreState(self._prefs._appState)
85 self.readPreferences()
86
87 def readPreferences(self):
88 bg = self._prefs.background
89 if bg and bg.isValid():
90 self.plotterWidget.qview.setBackgroundBrush(QtGui.QBrush(bg))
91 pen_colors = [self._prefs["color%d"%i] for i in range(8)]
92 self.plotterWidget.pen_colors = pen_colors
93
94 def replotCurrent(self):
95 self.currentPlotChanged(self.plotsView.currentIndex())
96
97 def setupUi(self):
98 form_class.setupUi(self, self) # call qtdesigner generated form creation
99 # actions defined in designer
100 self.connect(self.actionPreferences, SIGNAL('triggered(bool)'),
101 self.preferencesTriggered)
102 self.connect(self.actionQuit, SIGNAL('triggered(bool)'),
103 self.quitTriggered)
104 self.actionQuit.setShortcut(QtGui.QKeySequence(u'Ctrl+Q'))
105 self.connect(self.actionOpen, SIGNAL('triggered(bool)'),
106 self.openTriggered)
107 self.actionOpen.setShortcut(QtGui.QKeySequence(u'Ctrl+O'))
108 self.connect(self.actionSave, SIGNAL('triggered(bool)'),
109 self.saveTriggered)
110 self.actionSave.setShortcut(QtGui.QKeySequence(u'Ctrl+S'))
111 self.connect(self.actionSaveAs, SIGNAL('triggered(bool)'),
112 self.saveAsTriggered)
113
114 self.plotterWidget = QHPGLPlotterWidget(self)
115 self.setCentralWidget(self.plotterWidget)
116
117 self.connect(self.captureButton, SIGNAL("toggled(bool)"),
118 self.captureToggled)
119
120 self._plots_list = QtGui.QStringListModel()
121 self.plotsView.setModel(self._plots_list)
122 self.connect(self.plotsView, SIGNAL('activated(const QModelIndex&)'),
123 self.currentPlotChanged)
124 self.connect(self.plotsView.selectionModel(),
125 SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)'),
126 self.currentPlotChanged)
127
128 def currentPlotChanged(self, index, old_index=None):
129 if index.isValid():
130 value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString())
131
132 self.plotterWidget.clear()
133 self.plotterWidget.parse(self._plots[value])
134
135 def preferencesTriggered(self, checked=False):
136 PreferencesEditor(self._prefs, self).exec_()
137 self.readPreferences()
138 self.replotCurrent()
139
140 def quitTriggered(self, checked=False):
141 self.close()
142
143 def closeEvent(self, event):
144 if 1:
145 #if self.promptForSave():
146 self._prefs._pos = self.pos()
147 self._prefs._size = self.size()
148 self._prefs._appState = self.saveState()
149 event.accept()
150 else:
151 event.ignore()
152
153 def openTriggered(self, checked=False):
154 filenames = QtGui.QFileDialog.getOpenFileNames(self, "Open a HPGL file to display", '.', 'HPGL files (*.plt)\nAll files (*)')
155 self.openFiles(filenames)
156 self.displayFirst()
157
158 def displayFirst(self):
159 if not self.plotsView.currentIndex().isValid():
160 self.plotsView.setCurrentIndex(self.plotsView.model().index(0, 0))
161
162 def openFiles(self, filenames):
163 ok = False
164 for filename in filenames:
165 filename = str(filename)
166 if os.path.exists(filename):
167 data = open(filename).read()
168 name = os.path.basename(filename)
169 name = os.path.splitext(name)[0]
170 lst = self.plotsView.model().stringList()
171 lst.append(name)
172 self._plots[name] = data
173 self.plotsView.model().setStringList(lst)
174 ok = True
175 return ok
176
177 def plotReceived(self, num):
178 self._receiving = False
179 self.setReceivingLed()
180 plot, timestamp = self.captureThread.getPlot(num)
181 name = "plot_%s"%(num)
182 lst = self.plotsView.model().stringList()
183 lst.append(name)
184 self._plots[name] = plot
185 self.plotsView.model().setStringList(lst)
186
187 def plotStarted(self):
188 self._receiving = True
189 self.setReceivingLed()
190
191 def saveTriggered(self, checked=False):
192 print "save"
193
194 def saveAsTriggered(self, checked=False):
195 index = self.plotsView.selectionModel().currentIndex()
196 if index.isValid():
197 filename = QtGui.QFileDialog.getSaveFileName(self, "Selecte a file name to save HPGL file", '.', 'HPGL files (*.plt)\nAll files (*)')
198 n = index.row()
199 value = unicode(self.plotsView.model().data(index, Qt.DisplayRole).toString())
200 open(filename, 'w').write(self._plots[value])
201
202
203 def initializeGPIB(self):
204 self._online = False
205 try:
206 self.gpib_plotter = QGPIBplotter(device=self._prefs.device,
207 address=self._prefs.address,
208 )
209 self.captureThread = GPIBReceiver(self.gpib_plotter)
210 self.connect(self.captureThread, SIGNAL('plotReceived(int)'),
211 self.plotReceived)
212 self.connect(self.captureThread, SIGNAL('plotStarted()'),
213 self.plotStarted)
214 self.captureThread.start()
215 except Exception, e:
216 #print e
217 self.gpib_plotter = None
218 self.setCaptureLed()
219
220 def captureToggled(self, state):
221 if state:
222 if self.gpib_plotter is None:
223 self.initializeGPIB()
224 if self.gpib_plotter is None:
225 QtGui.QMessageBox.critical(self, self.tr("GPIB error"),
226 self.tr("<b>Unable to initialize GPIB connection</b>.<br>Please check your GPIB dongle and settings."))
227 self._online = False
228 self.setCaptureLed()
229 return
230 self._online = True
231 self.captureThread.startCapture()
232 else:
233 if self.captureThread:
234 self.captureThread.stopCapture()
235 self._online = False
236 self.setCaptureLed()
237
238 def setCaptureLed(self):
239 if self._online:
240 icn = QtGui.QIcon(':/icons/led_green.svg')
241 else:
242 icn = QtGui.QIcon(':/icons/led_green_off.svg')
243 self.captureButton.setIcon(icn)
244 self.captureButton.setChecked(self._online)
245
246 def setReceivingLed(self):
247 if self._receiving:
248 icn = QtGui.QIcon(':/icons/led_red.svg')
249 else:
250 icn = QtGui.QIcon(':/icons/led_red_off.svg')
251 self.receivingButton.setIcon(icn)
252
253 class QGPIBplotter(GPIBplotter):
254 def __init__(self, device="/dev/ttyUSB0", baudrate=115200, timeout=0.1,
255 address=5):
256 GPIBplotter.__init__(self, device, baudrate, timeout, address)
257 self.emitter = None
258
259 def plotStarted(self):
260 if self.emitter:
261 self.emitter.emit(SIGNAL('plotStarted()'))
262 #self.emitter.msleep(1)
263
264 class GPIBReceiver(QtCore.QThread):
265 def __init__(self, cnx):
266 QtCore.QThread.__init__(self)
267 self.gpibplotter = cnx
268 self.gpibplotter.emitter = self
269
270 self._cancelmutex = QtCore.QMutex()
271 self._cancel = False
272 #self._nreceived = 0
273 self._plotsmutex = QtCore.QMutex()
274 self._plots = []
275 self._startstopmutex = QtCore.QMutex()
276 self._startstop = QtCore.QWaitCondition()
277 self._capturing = False
278
279 def cancel(self):
280 self._cancelmutex.lock()
281 self._cancel = True
282 self._cancelmutex.unlock()
283
284 def startCapture(self):
285 self._startstop.wakeOne()
286
287 def stopCapture(self):
288 self._startstopmutex.lock()
289 self._capturing = False
290 self._startstopmutex.unlock()
291
292 def run(self):
293 while 1:
294 self._cancelmutex.lock()
295 if self._cancel:
296 return
297 self._cancelmutex.unlock()
298 self._startstopmutex.lock()
299 if not self._capturing:
300 self._startstop.wait(self._startstopmutex)
301 self._capturing = True
302 self._startstopmutex.unlock()
303
304 plot = self.gpibplotter.load_plot(wait_timeout=0.1)
305 timestamp = time.time()
306 if plot:
307 self._plotsmutex.lock()
308 self._plots.append((plot, timestamp))
309 n = len(self._plots)
310 self._plotsmutex.unlock()
311 self.emit(SIGNAL('plotReceived(int)'), n-1)
312 self.msleep(10)
313
314 def getPlot(self, num):
315 self._plotsmutex.lock()
316 try:
317 return self._plots[num]
318 finally:
319 self._plotsmutex.unlock()
320
321 def main():
322 import optparse
323 opt = optparse.OptionParser('A simple PyQt4 HP7470A GPIB plotter emulator for USB-GPIB bundle (ProLogix)')
324 opt.add_option('-m', '--mockup', default=False,
325 action="store_true",
326 dest='mockup',
327 help='Use a pseudo GPIB connection (for test purpose)',
328 )
329 opt.add_option('-v', '--verbose', default=False,
330 action="store_true",
331 dest="verbose",
332 help="Verbose mode",)
333
334 options, argv = opt.parse_args(sys.argv)
335
336 a = QtGui.QApplication(argv)
337 w = QtHPGLPlotter()
338 files = [f for f in argv[1:] if os.path.isfile(f)]
339 files.sort(cmp=str_num_cmp)
340 if w.openFiles(files):
341 w.displayFirst()
342
343 w.show()
344 a.exec_()
345
346 if __name__ == '__main__':
347 main()

mercurial