def showMessage(self, text): #bruce 081230 """ [extends superclass method] """ ## QStatusBar.showMessage(self, " ") QStatusBar.showMessage(self, text) ## print "message was set to %r" % str(self.currentMessage()) return
class MainWidget(QWidget): def __init__(self): QWidget.__init__(self) # define periodic table widget for element selection self.periodicTableWidget = widgets.PeriodicTableDialog() # initial molecule Zmatrix (can be empty) # self.inp = [] self.inp = [['H'], ['O', 1, 0.9], ['O', 2, 1.4, 1, 105.], ['H', 3, 0.9, 2, 105., 1, 120.]] self.atomList = [] self.highList = [] self.labelList = [] self.fast = False # define & initialize ZMatModel that will contain Zmatrix data self.ZMatModel = QStandardItemModel(len(self.inp), 7, self) self.ZMatTable = QTableView(self) self.ZMatTable.setModel(self.ZMatModel) self.ZMatTable.setFixedWidth(325) #self.ZMatTable.installEventFilter(self) #self.ZMatModel.installEventFilter(self) self.ZMatModel.setHorizontalHeaderLabels(['atom','','bond','','angle','','dihedral']) for j, width in enumerate([40, 22, 65, 22, 65, 22, 65]): self.ZMatTable.setColumnWidth(j, width) # populate the ZMatModel self.populateZMatModel() #define Menu bar menus and their actions self.menuBar = QMenuBar(self) fileMenu = self.menuBar.addMenu('&File') editMenu = self.menuBar.addMenu('&Edit') viewMenu = self.menuBar.addMenu('&View') measureMenu = self.menuBar.addMenu('&Measure') helpMenu = self.menuBar.addMenu('&Help') readZmatAction = QAction('&Read &ZMat', self) readZmatAction.setShortcut('Ctrl+O') readZmatAction.setStatusTip('Read Zmat from file') readZmatAction.triggered.connect(self.readZmat) fileMenu.addAction(readZmatAction) readXYZAction = QAction('&Read &XYZ', self) readXYZAction.setShortcut('Ctrl+Shift+O') readXYZAction.setStatusTip('Read XYZ from file') readXYZAction.triggered.connect(self.readXYZ) fileMenu.addAction(readXYZAction) readGaussianAction = QAction('&Read &Gaussian log', self) readGaussianAction.setShortcut('Ctrl+G') readGaussianAction.setStatusTip('Read Gaussian log file') readGaussianAction.triggered.connect(self.readGaussian) fileMenu.addAction(readGaussianAction) writeZmatAction = QAction('&Write &ZMat', self) writeZmatAction.setShortcut('Ctrl+S') writeZmatAction.setStatusTip('Write Zmat to file') writeZmatAction.triggered.connect(self.writeZmat) fileMenu.addAction(writeZmatAction) writeXYZAction = QAction('&Write &XYZ', self) writeXYZAction.setShortcut('Ctrl+Shift+S') writeXYZAction.setStatusTip('Write XYZ from file') writeXYZAction.triggered.connect(self.writeXYZ) fileMenu.addAction(writeXYZAction) exitAction = QAction('&Exit', self) exitAction.setShortcut('Ctrl+Q') exitAction.setStatusTip('Exit application') exitAction.triggered.connect(qApp.quit) fileMenu.addAction(exitAction) addRowAction = QAction('&Add &row', self) addRowAction.setShortcut('Ctrl+R') addRowAction.setStatusTip('Add row to ZMatrix') addRowAction.triggered.connect(self.addRow) editMenu.addAction(addRowAction) deleteRowAction = QAction('&Delete &row', self) deleteRowAction.setShortcut('Ctrl+Shift+R') deleteRowAction.setStatusTip('Delete row from ZMatrix') deleteRowAction.triggered.connect(self.deleteRow) editMenu.addAction(deleteRowAction) addAtomAction = QAction('&Add &atom', self) addAtomAction.setShortcut('Ctrl+A') addAtomAction.setStatusTip('Add atom to ZMatrix') addAtomAction.triggered.connect(self.buildB) editMenu.addAction(addAtomAction) drawModeMenu = QMenu('Draw mode', self) viewMenu.addMenu(drawModeMenu) fastDrawAction = QAction('&Fast draw', self) fastDrawAction.triggered.connect(self.fastDraw) normalDrawAction = QAction('&Normal draw', self) normalDrawAction.triggered.connect(self.normalDraw) drawModeMenu.addAction(normalDrawAction) drawModeMenu.addAction(fastDrawAction) clearHighlightsAction = QAction('&Clear selection', self) clearHighlightsAction.setShortcut('Ctrl+C') clearHighlightsAction.setStatusTip('Clear highlighted atoms') clearHighlightsAction.triggered.connect(self.clearHighlights) viewMenu.addAction(clearHighlightsAction) clearLabelsAction = QAction('&Clear labels', self) clearLabelsAction.setShortcut('Ctrl+Alt+C') clearLabelsAction.setStatusTip('Clear labels') clearLabelsAction.triggered.connect(self.clearLabels) viewMenu.addAction(clearLabelsAction) clearUpdateViewAction = QAction('&Clear selection and labels', self) clearUpdateViewAction.setShortcut('Ctrl+Shift+C') clearUpdateViewAction.setStatusTip('Clear highlighted atoms and labels') clearUpdateViewAction.triggered.connect(self.clearUpdateView) viewMenu.addAction(clearUpdateViewAction) self.showGaussAction = QAction('Show &Gaussian geometry optimization', self) self.showGaussAction.setShortcut('Ctrl+G') self.showGaussAction.setStatusTip('Show Gaussian geometry optimization plots for energy, force and displacement.') self.showGaussAction.setEnabled(False) self.showGaussAction.triggered.connect(self.showGauss) viewMenu.addAction(self.showGaussAction) self.showFreqAction = QAction('Show &IR frequency plot', self) self.showFreqAction.setShortcut('Ctrl+I') self.showFreqAction.setStatusTip('Show Gaussian calculated IR frequency plot.') self.showFreqAction.setEnabled(False) self.showFreqAction.triggered.connect(self.showFreq) viewMenu.addAction(self.showFreqAction) measureDistanceAction = QAction('&Measure &distance', self) measureDistanceAction.setShortcut('Ctrl+D') measureDistanceAction.setStatusTip('Measure distance between two atoms') measureDistanceAction.triggered.connect(self.measureDistanceB) measureMenu.addAction(measureDistanceAction) measureAngleAction = QAction('&Measure &angle', self) measureAngleAction.setShortcut('Ctrl+Shift+D') measureAngleAction.setStatusTip('Measure angle between three atoms') measureAngleAction.triggered.connect(self.measureAngleB) measureMenu.addAction(measureAngleAction) aboutAction = QAction('&About', self) aboutAction.setStatusTip('About this program...') aboutAction.triggered.connect(self.about) helpMenu.addAction(aboutAction) aboutQtAction = QAction('&About Qt', self) aboutQtAction.setStatusTip('About Qt...') aboutQtAction.triggered.connect(self.aboutQt) helpMenu.addAction(aboutQtAction) # define GL widget that displays the 3D molecule model self.window = widgets.MyGLView() self.window.installEventFilter(self) self.window.setMinimumSize(500, 500) #self.window.setBackgroundColor((50, 0, 10)) self.updateView() self.gaussianPlot = GraphicsLayoutWidget() self.gaussianPlot.resize(750, 250) self.gaussianPlot.setWindowTitle('Gaussian geometry optimization') #self.gaussianPlot.setAspectLocked(True) #self.gaussianPlot.addLayout(rowspan=3, colspan=1) self.FreqModel = QStandardItemModel(1, 3, self) self.freqTable = QTableView(self) self.freqTable.setModel(self.FreqModel) self.freqTable.setMinimumWidth(240) self.freqTable.installEventFilter(self) self.FreqModel.installEventFilter(self) self.FreqModel.setHorizontalHeaderLabels(['Frequency','IR Intensity','Raman Intensity']) for j, width in enumerate([80, 80, 80]): self.freqTable.setColumnWidth(j, width) self.freqWidget = QWidget() self.freqWidget.setWindowTitle('IR frequency plot & table') self.freqWidget.resize(800, 400) self.freqWidget.layout = QHBoxLayout(self.freqWidget) self.freqWidget.layout.setSpacing(1) self.freqWidget.layout.setContentsMargins(1, 1, 1, 1) self.freqPlot = GraphicsLayoutWidget() self.freqWidget.layout.addWidget(self.freqPlot) self.freqWidget.layout.addWidget(self.freqTable) self.freqTable.clicked.connect(self.freqCellClicked) # define other application parts self.statusBar = QStatusBar(self) self.fileDialog = QFileDialog(self) # define application layout self.layout = QVBoxLayout(self) self.layout.setSpacing(1) self.layout.setContentsMargins(1, 1, 1, 1) self.layout1 = QHBoxLayout() self.layout1.setSpacing(1) self.layout1.addWidget(self.ZMatTable) self.layout1.addWidget(self.window) self.layout.addWidget(self.menuBar) self.layout.addLayout(self.layout1) self.layout.addWidget(self.statusBar) self.adjustSize() self.setWindowTitle('Moldy') iconPath = 'icon.png' icon = QIcon(iconPath) icon.addFile(iconPath, QSize(16, 16)) icon.addFile(iconPath, QSize(24, 24)) icon.addFile(iconPath, QSize(32, 32)) icon.addFile(iconPath, QSize(48, 48)) icon.addFile(iconPath, QSize(256, 256)) self.setWindowIcon(icon) # start monitoring changes in the ZMatModel self.ZMatModel.dataChanged.connect(self.clearUpdateView) # run and show the application def run(self): self.show() self.ZMatTable.clicked.connect(self.ZMatCellClicked) qt_app.instance().aboutToQuit.connect(self.deleteGLwidget) qt_app.exec_() # fill the ZMatModel with initial data from 'self.inp' def populateZMatModel(self): self.ZMatModel.removeRows(0, self.ZMatModel.rowCount()) for i, row in enumerate(self.inp): for j, cell in enumerate(row): item = QStandardItem(str(cell)) self.ZMatModel.setItem(i, j, item) # some cells should not be editable, they are disabled for i in range(min(len(self.inp), 3)): for j in range(2*i+1, 7): self.ZMatModel.setItem(i, j, QStandardItem()) self.ZMatModel.item(i, j).setBackground(QColor(150,150,150)) self.ZMatModel.item(i, j).setFlags(Qt.ItemIsEnabled) def populateFreqModel(self): self.FreqModel.removeRows(0, self.FreqModel.rowCount()) for i, row in enumerate(zip(self.vibfreqs, self.vibirs, self.vibramans)): for j, cell in enumerate(row): item = QStandardItem(str(cell)) self.FreqModel.setItem(i, j, item) # add a row to the bottom of the ZMatModel def addRow(self): # temporarily stop updating the GL window self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) row = self.ZMatModel.rowCount() self.ZMatModel.insertRow(row) # some cells should not be editable if row < 3: for j in range(2*row+1, 7): self.ZMatModel.setItem(row, j, QStandardItem()) self.ZMatModel.item(row, j).setBackground(QColor(150,150,150)) self.ZMatModel.item(row, j).setFlags(Qt.ItemIsEnabled) # restart GL window updating self.ZMatModel.dataChanged.connect(self.clearUpdateView) self.statusBar.clearMessage() self.statusBar.showMessage('Added 1 row.', 3000) # delete the last row of the ZMatModel def deleteRow(self): xyz = [list(vi) for vi in list(v)] atoms = [str(elements[e]) for e in elems] oldLen = self.ZMatModel.rowCount() idxs = sorted(set(idx.row() for idx in self.ZMatTable.selectedIndexes()), reverse=True) newLen = oldLen - len(idxs) if newLen == oldLen: self.ZMatModel.removeRow(self.ZMatModel.rowCount()-1) else: self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) for idx in idxs: self.ZMatModel.removeRow(idx) if idx < 3: for i in range(idx, min(3, newLen)): for j in range(2*i+1, 7): self.ZMatModel.setItem(i, j, QStandardItem()) self.ZMatModel.item(i, j).setBackground(QColor(150,150,150)) self.ZMatModel.item(i, j).setFlags(Qt.ItemIsEnabled) if len(xyz) > idx: xyz.pop(idx) atoms.pop(idx) self.inp = xyz2zmat(xyz, atoms) self.populateZMatModel() for i in reversed(self.highList): self.window.removeItem(i[1]) self.highList = [] self.ZMatModel.dataChanged.connect(self.clearUpdateView) self.updateView() self.statusBar.clearMessage() if idxs: self.statusBar.showMessage('Deleted row(s): '+str([i+1 for i in idxs]), 3000) else: self.statusBar.showMessage('Deleted last row.', 3000) # show the periodic table widget def periodicTable(self): self.statusBar.clearMessage() self.statusBar.showMessage('Select element from periodic table.') self.periodicTableWidget.exec_() selection = self.periodicTableWidget.selection() return selection # import molecule with zmatrix coordinates def readZmat(self): self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) filename = self.fileDialog.getOpenFileName(self, 'Open file', expanduser('~'), '*.zmat;;*.*') self.inp = [] self.populateZMatModel() if filename: with open(filename, 'r') as f: next(f) next(f) for row in f: self.inp.append(row.split()) f.close() self.populateZMatModel() self.ZMatModel.dataChanged.connect(self.clearUpdateView) self.updateView() self.statusBar.clearMessage() self.statusBar.showMessage('Read molecule from '+filename+'.', 5000) self.showGaussAction.setEnabled(False) self.showFreqAction.setEnabled(False) # import molecule with xyz coordinates def readXYZ(self): self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) filename = self.fileDialog.getOpenFileName(self, 'Open file', expanduser('~'), '*.xyz;;*.*') xyz = [] elems = [] self.inp = [] self.populateZMatModel() if filename: with open(filename, 'r') as f: next(f) next(f) for row in f: rs = row.split() if len(rs) == 4: elems.append(rs[0]) xyz.append([float(f) for f in rs[1:]]) f.close() self.inp = xyz2zmat(xyz, elems) self.populateZMatModel() #print(elems) self.ZMatModel.dataChanged.connect(self.clearUpdateView) self.updateView() self.statusBar.clearMessage() self.statusBar.showMessage('Read molecule from '+filename+'.', 5000) self.showGaussAction.setEnabled(False) self.showFreqAction.setEnabled(False) # import Gaussian log file def readGaussian(self): global vsShifted self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) filename = self.fileDialog.getOpenFileName(self, 'Open file', expanduser('~'), '*.log;;*.*') if filename: self.gaussianPlot.clear() self.inp = [] self.populateZMatModel() file = ccopen(filename) data = file.parse().getattributes() self.natom = data['natom'] self.atomnos = data['atomnos'].tolist() self.atomsymbols = [ str(elements[e]) for e in self.atomnos ] self.atomcoords = data['atomcoords'].tolist() self.scfenergies = data['scfenergies'].tolist() self.geovalues = data['geovalues'].T.tolist() self.geotargets = data['geotargets'].tolist() if 'vibfreqs' in data.keys(): self.vibfreqs = data['vibfreqs'] #print(self.vibfreqs) self.vibirs = data['vibirs'] #print(self.vibirs) #print(data.keys()) if 'vibramans' in data.keys(): self.vibramans = data['vibramans'] else: self.vibramans = [''] * len(self.vibirs) self.vibdisps = data['vibdisps'] #print(self.vibdisps) self.inp = xyz2zmat(self.atomcoords[0], self.atomsymbols) self.populateZMatModel() titles = ['SCF Energies', 'RMS & Max Forces', 'RMS & Max Displacements'] for i in range(3): self.gaussianPlot.addPlot(row=1, col=i+1) plot = self.gaussianPlot.getItem(1, i+1) plot.setTitle(title=titles[i]) if i == 0: c = ['c'] x = [0] y = [self.scfenergies] else: c = ['r', 'y'] x = [0, 0] y = [self.geovalues[2*i-2], self.geovalues[2*i-1]] targety = [self.geotargets[2*i-2], self.geotargets[2*i-1]] plot.clear() plot.maxData = plot.plot(y[0], symbol='o', symbolPen=c[0], symbolBrush=c[0], pen=c[0], symbolSize=5, pxMode=True, antialias=True, autoDownsample=False) plot.highlight=plot.plot(x, [ yy[0] for yy in y ], symbol='o', symbolPen='w', symbolBrush=None, pen=None, symbolSize=15, pxMode=True, antialias=True, autoDownsample=False) plot.maxData.sigPointsClicked.connect(self.gausclicked) if i > 0: for j in range(2): plot.addLine(y=np.log10(targety[j]), pen=mkPen((255, 255*j, 0, int(255/2)), width=1)) plot.RMSData=plot.plot(y[1], symbol='o', symbolPen=c[1], symbolBrush=c[1], pen=c[1], symbolSize=5, pxMode=True, antialias=True, autoDownsample=False) plot.RMSData.sigPointsClicked.connect(self.gausclicked) plot.setLogMode(y=True) self.showGauss() self.updateView() self.statusBar.clearMessage() self.statusBar.showMessage('Read molecule from '+filename+'.', 5000) self.ZMatModel.dataChanged.connect(self.clearUpdateView) if self.natom: self.showGaussAction.setEnabled(True) if 'vibfreqs' in data.keys(): self.showFreqAction.setEnabled(True) # populate the FreqModel self.populateFreqModel() self.freqPlot.clear() irPlot = self.freqPlot.addPlot(row=1, col=1) irPlot.clear() minFreq = np.min(self.vibfreqs) maxFreq = np.max(self.vibfreqs) maxInt = np.max(self.vibirs) x = np.sort(np.concatenate([np.linspace(minFreq-100, maxFreq+100, num=1000), self.vibfreqs])) y = x*0 for f,i in zip(self.vibfreqs, self.vibirs): y += lorentzv(x, f, 2*np.pi, i) #xy = np.array([np.concatenate([x, np.array(self.vibfreqs)]), np.concatenate([y, np.array(self.vibirs)])]).T #xysort = xy[xy[:,0].argsort()] irPlot.maxData = irPlot.plot(x, y, antialias=True) markers = ErrorBarItem(x=self.vibfreqs, y=self.vibirs, top=maxInt/30, bottom=None, pen='r') irPlot.addItem(markers) self.showFreq() #self.vibdisps = np.append(self.vibdisps, [np.mean(self.vibdisps, axis=0)], axis=0) maxt = 100 vsShifted = np.array([ [ vs + self.vibdisps[i]*np.sin(t*2*np.pi/maxt)/3 for t in range(maxt) ] for i in range(len(self.vibfreqs)) ]) else: self.showFreqAction.setEnabled(False) self.freqWidget.hide() def showGauss(self): self.gaussianPlot.show() def showFreq(self): self.freqWidget.show() # export Zmatrix to csv def writeZmat(self): zm = model2list(self.ZMatModel) filename = self.fileDialog.getSaveFileName(self, 'Save file', expanduser('~')+'/'+getFormula(list(list(zip(*zm))[0]))+'.zmat', '*.zmat;;*.*') try: filename except NameError: pass else: if filename: writeOutput(zm, filename) self.statusBar.clearMessage() self.statusBar.showMessage('Wrote molecule to '+filename+'.', 5000) # export XYZ coordinates to csv def writeXYZ(self): xyz = [] zm = model2list(self.ZMatModel) for i in range(len(v)): xyz.append(np.round(v[i], 7).tolist()) xyz[i][:0] = zm[i][0] if len(v) > 0: formula = getFormula(list(list(zip(*xyz))[0])) else: formula = 'moldy_output' filename = self.fileDialog.getSaveFileName(self, 'Save file', expanduser('~')+'/'+formula+'.xyz', '*.xyz;;*.*') try: filename except NameError: pass else: if filename: writeOutput(xyz, filename) self.statusBar.clearMessage() self.statusBar.showMessage('Wrote molecule to '+filename+'.', 5000) # redraw the 3D molecule in GL widget def updateView(self): global r global c global v global vs global elems global nelems data = model2list(self.ZMatModel) try: # create a list with element coordinates v = zmat2xyz(data) except (AssertionError, IndexError, ZMError): pass else: # clear the screen before redraw for item in reversed(self.window.items): self.window.removeItem(item) # create a second coordinate list 'vs' that is centered in the GL view self.atomList = [] if len(v) > 0: shift = np.mean(v, axis=0) vs = np.add(v, -shift) elems = [ 1 + next((i for i, sublist in enumerate(colors) if row[0] in sublist), -1) for row in data ] nelems = len(elems) # define molecule radii and colors r = [] c = [] for i in elems: r.append(elements[i].covalent_radius) c.append(colors[i-1][-1]) # draw atoms for i in range(nelems): addAtom(self.window, i, r, vs, c, fast=self.fast) self.atomList.append([i, self.window.items[-1]]) #print(self.atomList) # draw bonds where appropriate combs = list(itertools.combinations(range(nelems), 2)) bonds = [] for i in combs: bonds.append(addBond(self.window, i[0], i[1], r, vs, c, fast=self.fast)) if self.fast: bondedAtoms = set(filter((None).__ne__, flatten(bonds))) for i in set(range(nelems)) - bondedAtoms: addUnbonded(self.window, i, vs, c) self.atomList[i][1]=self.window.items[-1] #print(self.atomList) for i in self.highList: self.window.addItem(i[1]) for i in self.labelList: self.window.addItem(i) if len(v) > 1: maxDim = float('-inf') for dim in v.T: span = max(dim)-min(dim) if span > maxDim: maxDim = span else: maxDim = 2 self.window.setCameraPosition(distance=maxDim*1.5+1) global index index = 0 def updateFreq(self): global vsShifted, index, r, c index += 1 index = index % len(vsShifted[0]) #print(index) #print(vsShifted[index]) for item in reversed(self.window.items): self.window.removeItem(item) for i in range(nelems): addAtom(self.window, i, r, vsShifted[self.freqIndex, index], c, fast=self.fast) self.atomList.append([i, self.window.items[-1]]) combs = itertools.combinations(range(nelems), 2) bonds = [] for i in combs: bonds.append(addBond(self.window, i[0], i[1], r, vsShifted[self.freqIndex, index], c, fast=self.fast)) if self.fast: bondedAtoms = set(filter((None).__ne__, flatten(bonds))) for i in set(range(nelems)) - bondedAtoms: addUnbonded(self.window, i, vsShifted[self.freqIndex, index], c) self.atomList[i][1]=self.window.items[-1] # detect mouse clicks in GL window and process them def eventFilter(self, obj, event): if obj == self.window: if event.type() == event.MouseButtonPress: itms = obj.itemsAt((event.pos().x()-2, event.pos().y()-2, 4, 4)) if len(itms): self.highlight(obj, [itms[0]]) elif len(self.atomList) == 0: self.build() # also do the default click action return super(MainWidget, self).eventFilter(obj, event) def ZMatCellClicked(self): idxs = sorted(set(idx.row() for idx in self.ZMatTable.selectedIndexes()), reverse=True) itms = [] if self.highList: highIdx = list(np.array(self.highList).T[0]) for idx in idxs: if self.highList and idx in highIdx: itms.append(self.highList[highIdx.index(idx)][1]) elif len(self.atomList) > idx: itms.append(self.atomList[idx][1]) self.highlight(self.window, itms) def freqCellClicked(self): global vsShifted self.timer = QTimer() self.timer.setInterval(30) self.timer.timeout.connect(self.updateFreq) idxs = [ idx.row() for idx in self.freqTable.selectedIndexes() ] if len(idxs) == 1: self.freqIndex = idxs[0] self.timer.stop() self.timer.timeout.connect(self.updateFreq) try: self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) except TypeError: pass self.timer.start() if len(idxs) != 1: self.timer.stop() self.freqTable.clearSelection() self.timer.timeout.disconnect(self.updateFreq) self.ZMatModel.dataChanged.connect(self.clearUpdateView) self.clearUpdateView() def gausclicked(self, item, point): itemdata = item.scatter.data points = [ row[7] for row in itemdata ] idx = points.index(point[0]) for i in range(3): if i == 0: x = [idx] y = [self.scfenergies[idx]] else: x = [idx, idx] y = [self.geovalues[2*i-2][idx], self.geovalues[2*i-1][idx]] plot = self.gaussianPlot.getItem(1, i+1) plot.removeItem(plot.highlight) plot.highlight=plot.plot(x, y, symbol='o', symbolPen='w', symbolBrush=None, pen=None, symbolSize=15, pxMode=True, antialias=True, autoDownsample=False) self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) self.inp = [] self.populateZMatModel() self.inp = xyz2zmat(self.atomcoords[min(idx, len(self.atomcoords)-1)], self.atomsymbols) self.populateZMatModel() self.ZMatModel.dataChanged.connect(self.clearUpdateView) self.updateView() def highlight(self, obj, itms): for itm in itms: idx = next((i for i, sublist in enumerate(self.atomList) if itm in sublist), -1) #print(idx) if idx != -1: addAtom(obj, idx, r, vs, c, opt='highlight', fast=self.fast) self.highList.append([idx, obj.items[-1]]) self.ZMatTable.selectRow(idx) idx = next((i for i, sublist in enumerate(self.highList) if itm in sublist), -1) if idx != -1: obj.removeItem(self.highList[idx][1]) self.highList.pop(idx) self.ZMatTable.clearSelection() self.statusBar.clearMessage() if len(self.highList) > 0: idxs = np.asarray(self.highList).T[0] selected = [] for i in idxs: selected.append(str(i+1)+str(elements[elems[i]])) self.statusBar.showMessage('Selected atoms: '+str(selected), 5000) def buildB(self): try: nelems except NameError: self.build() else: if len(self.highList) <= min(nelems, 3): diff = min(nelems, 3) - len(self.highList) if diff != 0: self.statusBar.clearMessage() self.statusBar.showMessage('Please select '+str(diff)+' more atom(s).') else: self.build() else: self.statusBar.clearMessage() self.statusBar.showMessage('Too many atoms selected.') def build(self): selection = self.periodicTable() row = self.ZMatModel.rowCount() self.addRow() self.ZMatModel.dataChanged.disconnect(self.clearUpdateView) newSymbol = selection[1] newData = [newSymbol] if len(self.highList) >= 1: newBond = round(2.1*gmean([ elements[e].covalent_radius for e in [selection[0], elems[self.highList[0][0]]] ]), 4) newData.append(self.highList[0][0]+1) newData.append(newBond) if len(self.highList) >= 2: newAngle = 109.4712 newData.append(self.highList[1][0]+1) newData.append(newAngle) if len(self.highList) == 3: newDihedral = 120. newData.append(self.highList[2][0]+1) newData.append(newDihedral) for j, cell in enumerate(newData): item = QStandardItem(str(cell)) self.ZMatModel.setItem(row, j, item) self.highList = [] self.ZMatModel.dataChanged.connect(self.clearUpdateView) self.updateView() def measureDistanceB(self): sel = len(self.highList) if sel <= 2: if sel < 2: self.statusBar.clearMessage() self.statusBar.showMessage('Please select '+str(2-sel)+' more atom(s).') else: self.measureDistance() else: self.statusBar.clearMessage() self.statusBar.showMessage('Too many atoms selected.') def measureDistance(self): pts = [] for pt in self.highList: pts.append(vs[pt[0]]) pts = np.array(pts) self.clearHighlights() line = gl.GLLinePlotItem(pos=pts, color=(0., 1., 0., 1.), width=3) self.window.addItem(line) self.labelList.append(line) q = pts[1]-pts[0] dist = round(np.sqrt(np.dot(q, q)), 4) self.window.labelPos.append(np.mean(pts[0:2], axis=0)) self.window.labelText.append(str(dist)) self.statusBar.clearMessage() self.statusBar.showMessage('Measured distance: '+str(dist)+' A.', 3000) def measureAngleB(self): sel = len(self.highList) if sel <= 3: if sel < 3: self.statusBar.clearMessage() self.statusBar.showMessage('Please select '+str(3-sel)+' more atom(s).') else: self.measureAngle() else: self.statusBar.clearMessage() self.statusBar.showMessage('Too many atoms selected.') def measureAngle(self): pts = [] for pt in self.highList: pts.append(vs[pt[0]]) pts = np.array(pts) q = pts[1]-pts[0] r = pts[2]-pts[0] q_u = q / np.sqrt(np.dot(q, q)) r_u = r / np.sqrt(np.dot(r, r)) angle = round(degrees(acos(np.dot(q_u, r_u))), 1) srange = np.array([slerp(q, r, t) for t in np.arange(0.0, 13/12, 1/12)]) self.clearHighlights() for i in range(12): mesh = gl.MeshData(np.array([[0,0,0],srange[i],srange[i+1]])) tri = gl.GLMeshItem(meshdata=mesh, smooth=False, computeNormals=False, color=(0.3, 1., 0.3, 0.5), glOptions=('translucent')) tri.translate(pts[0][0], pts[0][1], pts[0][2]) self.window.addItem(tri) self.labelList.append(tri) self.window.labelPos.append(slerp(q, r, 0.5)+pts[0]) self.window.labelText.append(str(angle)) self.statusBar.clearMessage() self.statusBar.showMessage('Measured angle: '+str(angle)+'°', 3000) def clearLabels(self): self.window.labelPos = [] self.window.labelText = [] self.labelList = [] self.updateView() def clearHighlights(self): for item in reversed(self.highList): self.window.removeItem(item[1]) self.highList = [] self.updateView() def clearUpdateView(self): self.window.labelPos = [] self.window.labelText = [] self.labelList = [] for item in reversed(self.highList): self.window.removeItem(item[1]) self.highList = [] self.updateView() #print(self.highList) def fastDraw(self): if not self.fast: self.fast = True self.updateView() def normalDraw(self): if self.fast: self.fast = False self.updateView() def about(self): QMessageBox.about(self, 'About moldy', 'moldy beta 15. 9. 2015') def aboutQt(self): QMessageBox.aboutQt(self, 'About Qt') def deleteGLwidget(self): self.window.setParent(None) del self.window
class DecoderMain(QtGui.QMainWindow, Ui_MainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) Ui_MainWindow.__init__(self) self.setupUi(self) #just sets the theme of the UI, in this case cleanlooks QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('cleanlooks')) #menu options self.actionExit.triggered.connect(self.closeApplication) self.actionSave.triggered.connect(self.saveOutput) #buttons #all other button actions are handled via the UI directly self.execute_btn.clicked.connect(self.decoderSelect) self.save_btn.clicked.connect(self.saveOutput) #turn on statusBar below self.statusBar = QStatusBar() self.setStatusBar(self.statusBar) self.updateStatus() #initially set the hash and length options to disabled, unless #if the proper function is chosen, then enable the options #there are two things here, both of which serve fine #one hides the entire group, the other disables it self.hash_options_group.hide() #self.hash_options_group.setEnabled(False) #this just deactivates, but doesn't hide self.length_group.hide() #if the user changes the combo box, run the function to #update the show/hide or enable/disabled status of the #hash options and/or length options self.func_select.currentIndexChanged.connect(self.enableOptions) #close the application, however that may happen def closeApplication(self): choice = QtGui.QMessageBox.question(self, 'Exit', 'Exit the Super Decoder?',\ QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) if choice == QtGui.QMessageBox.Yes: exit() else: return def updateStatus(self): message = 'Input Length: {} Output Length: {}'.\ format(len(self.input_line.text()),len(self.output_box.toPlainText())) self.statusBar.showMessage(message) #generic popup window for messages and good times def popupWindow(self, title, message): self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Information) self.msg.setWindowTitle(title) self.msg.setText(message) self.msg.setStandardButtons(QMessageBox.Ok) self.msg.exec_() #save whatever output data there is to a text file #if there is none, it won't save def saveOutput(self): output_text = unicode(self.output_box.toPlainText()) if len(output_text) != 0: output_text = 'Input Text:\n{}\n\nOutput Text:\n{}'.format( self.input_line.text(), output_text) export_name = QtGui.QFileDialog.getSaveFileName( filter=self.tr("Text file (*.txt)")) if export_name != "": f = open(export_name, 'wb') f.write(output_text) f.close() self.popupWindow( 'File Saved', 'Data has been saved to {}. '.format(export_name)) else: self.popupWindow('No Data for Export', 'Sorry, there is no data to save.') #enable or disable the options groups for different functions def enableOptions(self): #hide or show hash options if self.func_select.currentText() == 'Hash Text': self.hash_options_group.show() else: self.hash_options_group.hide() #hide or show length options, depending on function #could do this as one big if statement, but... if self.func_select.currentText() == 'Hex to ASCII': self.length_group.show() self.pad_radio.hide() elif self.func_select.currentText() == 'Base64 Decode': self.length_group.show() self.pad_radio.show() elif self.func_select.currentText() == 'Reverse Nibble': self.length_group.show() self.pad_radio.show() elif self.func_select.currentText() == 'Switch Endianness': self.length_group.show() self.pad_radio.show() elif self.func_select.currentText() == 'Hex to Decimal IP': self.length_group.hide() self.pad_radio.hide() else: self.length_group.hide() #checks the state of the combo box "func_select" #to determine which function to run def decoderSelect(self): self.updateStatus() if self.func_select.currentText() == 'Decimal to Hex': self.decimaltoHex() elif self.func_select.currentText() == 'Decimal to Binary': self.decimaltoBinary() elif self.func_select.currentText() == 'ASCII to Hex': self.asciitoHex() elif self.func_select.currentText() == 'Hex to ASCII': self.hextoAscii() elif self.func_select.currentText() == 'Base64 Encode': self.base64Encode() elif self.func_select.currentText() == 'Base64 Decode': self.base64Decode() elif self.func_select.currentText() == 'Reverse Nibble': self.reverseNibble() elif self.func_select.currentText() == 'Switch Endianness': self.switchEndian() elif self.func_select.currentText() == 'ROT13': self.rot13() elif self.func_select.currentText() == 'Hash Text': self.hashText() elif self.func_select.currentText() == 'Find OUI Vendor': self.findOUIVendor() elif self.func_select.currentText() == 'Hex to Decimal IP': self.hexToDecIP() #convert decimal to hex, pad with leading zero if necessary def decimaltoHex(self): try: input_num = int(self.input_line.text()) except ValueError: self.popupWindow('Invalid Input', 'Sorry, input is not proper decimal. ') self.output_box.clear() return hex_num = hex(input_num)[2:] hex_num = '0' * (len(hex_num) % 2) + hex_num self.output_box.setText(hex_num.rstrip('L')) self.updateStatus() #convert decimal to binary, pad zeroes depending on bit length def decimaltoBinary(self): try: input_num = int(self.input_line.text()) except ValueError: self.popupWindow('Invalid Input', 'Sorry, input is not proper decimal. ') self.output_box.clear() return bits = input_num.bit_length() zero_pad = '0' * (4 - (bits % 4)) bin_num = bin(input_num)[2:] bin_num = zero_pad + bin_num self.output_box.setText(bin_num) self.updateStatus() #encode base64 def base64Encode(self): input_text = unicode(self.input_line.text()) output_text = b64encode(input_text) self.output_box.setText(output_text) self.updateStatus() #decode base64, check length, etc. def base64Decode(self): input_text = unicode(self.input_line.text()) #check if the input has a length that's a multiple of 4 #pad if necessary if len(input_text) % 4 != 0: pad_length = len(input_text) % 4 input_text += '=' * pad_length self.input_line.setText(input_text) try: output_text = b64decode(input_text) except TypeError: self.output_box.clear() self.popupWindow('Invalid Input', 'Sorry, input is not proper base64. ') return self.output_box.setText(output_text) self.updateStatus() #reverse nibble stuff, check length def reverseNibble(self): input_text = unicode(self.input_line.text()) #check to see if input length is multiple of 2 #depending on the radio button selected, it #will truncate, pad, or refuse to decode if len(input_text) % 2: if self.truncate_radio.isChecked(): self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Truncating. ') elif self.pad_radio.isChecked(): self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Padding with "F". ') input_text += "F" elif self.refuse_radio.isChecked(): self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Failure to decode. ') self.output_box.clear() return output_text = ''.join([y + x for x, y in zip(*[iter(input_text)] * 2)]) self.output_box.setText(output_text) self.updateStatus() #switch from LE to BE and vice versa def switchEndian(self): input_text = unicode(self.input_line.text()) if len(input_text) == 0: return if len(input_text) % 2: if self.truncate_radio.isChecked(): input_text = input_text[:-1] self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Truncating. ') elif self.pad_radio.isChecked(): input_text += 'F' self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Padding with "F". ') elif self.refuse_radio.isChecked(): self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Failure to decode. ') self.output_box.clear() return self.output_box.setText("".join( reversed( [input_text[i:i + 2] for i in range(0, len(input_text), 2)]))) self.updateStatus() #get all the hashes of the input text def hashText(self): input_text = unicode(self.input_line.text()) output_text = '' if self.crc32_check.isChecked(): crc32_hash = hex((crc32(input_text) + (1 << 32)) % (1 << 32))[2:-1].upper().zfill(8) output_text += 'CRC32 Hash: {}\n'.format(crc32_hash) if self.adler_check.isChecked(): adler32_hash = hex(adler32(input_text))[2:].upper().zfill(8) output_text += 'Adler32 Hash: {}\n'.format(adler32_hash) if self.md5_check.isChecked(): md5_hash = md5(input_text).hexdigest() output_text += 'MD5 Hash: {}\n'.format(md5_hash) if self.sha1_check.isChecked(): sha1_hash = sha1(input_text).hexdigest() output_text += 'SHA1 Hash: {}\n'.format(sha1_hash) if self.sha256_check.isChecked(): sha256_hash = sha256(input_text).hexdigest() output_text += 'SHA256 Hash: {}\n'.format(sha256_hash) if self.b64_256_check.isChecked(): sha256_64_hash = b64encode(sha256(input_text).digest()) output_text += 'Base64 SHA256 Hash: {}\n'.format(sha256_64_hash) self.output_box.setText(output_text.rstrip()) self.updateStatus() #get a vendor for a given mac address or OUI using sqlite db def findOUIVendor(self): #remove colons, dashes, uppercase and only take first 3 bytes (6 characters when it's text) input_text = unicode(self.input_line.text()).replace(':', '').replace( '-', '').upper()[0:6] #just gonna see if it's hex or not by trying to int it try: int(input_text, 16) except ValueError: self.popupWindow('Invalid Input', 'Sorry, input is not a proper MAC or OUI. ') self.output_box.clear() return result = mutator(input_text) output_text = 'Original OUI: {}\nMatching OUI: {}\nVendor: {}\nMutation: {}\n'.\ format(result[0], result[1], result[2], result[3]) self.output_box.setText(output_text) self.updateStatus() #convert ascii to hex def asciitoHex(self): input_text = self.input_line.text().encode('utf8') output_text = hexlify(input_text).upper() self.output_box.setText(output_text) self.updateStatus() #convert hex to ascii, check for validity def hextoAscii(self): valid_chars = 'ABCDEF0123456789' input_text = unicode(self.input_line.text()) if all(c in valid_chars for c in input_text): if len(input_text) % 2: if self.truncate_radio.isChecked(): self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Truncated. ') input_text = input_text[:-1] elif self.refuse_radio.isChecked(): self.popupWindow('Improper Input Length',\ 'Input length is not a multiple of 2. Failure to decode. ') self.output_box.clear() return #check for valid characters (A-F and 0-9) from valid_chars above output_text = str(unhexlify(input_text)) self.output_box.setText(output_text) self.updateStatus() else: self.popupWindow('Invalid Input', 'Sorry, input is not proper hexadecimal. ') self.output_box.clear() def rot13(self): try: input_text = unicode(self.input_line.text()).encode('ascii') except UnicodeEncodeError: self.popupWindow('Invalid Input', 'Sorry, input is not properly formatted. ') self.output_box.clear() return rot13 = maketrans("ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz",\ "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") output_text = translate(input_text, rot13) self.output_box.setText(output_text) self.updateStatus() #needs error checking... not finished def hexToDecIP(self): input_text = str(self.input_line.text()) flipped_ip = ("".join( reversed( [input_text[i:i + 2] for i in range(0, len(input_text), 2)]))) output_text = ":".join( [str(int(flipped_ip[x:x + 2], 16)) for x in range(0, 8, 2)]) self.output_box.setText(output_text)
class LayoutWindow(QWidget): SLIDER_RESOLUTION = 1000 DIAL_RESOLUTION = 50 def __init__(self,parent=None): QWidget.__init__(self,parent) self.initWidgets() def initWidgets(self): self.ui_VideoFrame = VideoWidget(self) self.ui_Slider = QtGui.QSlider(QtCore.Qt.Horizontal) #contribution: #self.ui_Slider.setStyleSheet(stylesheet(self)) self.ui_Slider.setMinimum(0) self.ui_Slider.setMaximum(self.SLIDER_RESOLUTION) self.ui_Slider.setToolTip("Video track") #self.ui_Slider.setTickPosition(Qt.TicksAbove) self.ui_Dial = QDial(self) self.ui_Dial.setProperty("value", 0) self.ui_Dial.setNotchesVisible(True) self.ui_Dial.setWrapping(False) self.ui_Dial.setNotchTarget(5.0) self.ui_Dial.setToolTip("Fine tuning") self.setDialResolution(self.DIAL_RESOLUTION) self.ui_GotoField = QSpinBox(self) self.ui_GotoField.setValue(1) self.ui_GotoField.setToolTip("Goto Frame") self.ui_InfoLabel = QLabel(self) changeBackgroundColor(self.ui_InfoLabel , "lightblue") self.ui_InfoLabel.setText("") self.ui_InfoLabel.setToolTip("Infos about the video position") self.ui_CB_Reencode = QCheckBox(self) self.ui_CB_Reencode.setText("Exact cut") self.ui_CB_Reencode.setChecked(False) self.ui_CB_Reencode.setToolTip("Exact cut is slow, but precise") self.ui_List = self.__createListWidget() #self._listSplitter = QSplitter() ->add as widget...+QVBoxLayout #self._listSplitter.addWidget(iconList) #status bar self.statusbar = QStatusBar(self) self.statusbar.setSizeGripEnabled(True) self.statusbar.showMessage("Idle") self.statusbar.addPermanentWidget(self.__createProgressBar()) self.setLayout(self.makeGridLayout()) self.adjustSize() def makeGridLayout(self): gridLayout = QGridLayout() self.ui_List.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Expanding) gridLayout.addWidget(self.ui_List,0,0,5,1) #row column, rowSpan, columnSpan gridLayout.addWidget(self.ui_VideoFrame,0,2,3,-1); gridLayout.addWidget(self.ui_GotoField,4,1,1,20) gridLayout.addWidget(self.ui_InfoLabel,4,26,1,90) gridLayout.addWidget(self.ui_CB_Reencode,4,121) gridLayout.addWidget(self.ui_Slider,5,0,1,120) gridLayout.addWidget(self.ui_Dial,5,121) gridLayout.addWidget(self.statusbar,6,0,1,122) return gridLayout def makeBoxLayout(self): vbox = QVBoxLayout() vbox.addWidget(self.ui_VideoFrame) vbox.addWidget(self.ui_InfoLabel ); slidehbox = QHBoxLayout() slidehbox.addWidget(self.ui_Slider) slidehbox.addWidget(self.ui_Dial) midHBox = QHBoxLayout() midHBox.addWidget(self.ui_List) self.ui_List.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Expanding) midHBox.addLayout(vbox) mainVBox = QVBoxLayout() mainVBox.addLayout(midHBox) mainVBox.addStretch(1) mainVBox.addLayout(slidehbox) return mainVBox def showInfo(self,text): self.ui_InfoLabel.setText(text) def showStatusMessage(self,text): self.statusbar.showMessage(text) def setDialResolution(self,resolution): self.ui_Dial.setMinimum(-resolution/2) self.ui_Dial.setMaximum(resolution/2) def syncSliderPos(self,pos): self.ui_Slider.setSliderPosition(pos) def setSliderTicks(self,ticks): self.ui_Slider.setSingleStep(ticks) self.ui_Slider.setPageStep(ticks*2) def setGotoFieldMaximum(self,count): self.ui_GotoField.setMaximum(count) def clearVideoFrame(self): self.ui_VideoFrame.showFrame(None) ### Marks def addCutMark(self,frame,cutEntry,rowIndex): item = QListWidgetItem() img = CVImage(frame).scaled(self.ui_VideoFrame.imageRatio*SIZE_ICON, SIZE_ICON) pix = QtGui.QPixmap.fromImage(img) item.setIcon(QIcon(pix)) if cutEntry.isStartMode(): item.setBackgroundColor(QColor(224,255,224)) else: item.setBackgroundColor(QColor(255,224,224)) self.ui_List.insertItem(rowIndex,item) text = "%s <br> F: %s<br> T: %s" %(cutEntry.modeString,str(cutEntry.frameNumber),str(cutEntry.getTimeString())) marker = QLabel(text) marker.setWordWrap(True) marker.layout() self.ui_List.setItemWidget(item,marker) self.ui_List.setIconSize(QSize(SIZE_ICON,SIZE_ICON)) #Forces an update! self.ui_List.setCurrentItem(item) def hookEvents(self,aVideoController): self.__videoController=aVideoController #for menu callbacks self.ui_Slider.valueChanged.connect(aVideoController.sliderMoved) self.ui_Dial.valueChanged.connect(aVideoController.dialChanged) self.ui_Dial.sliderReleased.connect(self.__resetDial) self.ui_GotoField.editingFinished.connect(self.__gotoFrame) self.ui_CB_Reencode.stateChanged.connect(aVideoController.setExactCut) self.statusMessenger = StatusDispatcher() self.connect(self.statusMessenger,self.statusMessenger.signal,self.showStatusMessage) self._hookListActions() def keyReleaseEvent(self,event): self.__resetDial() def _hookListActions(self): #TOO bad-the list model -should be here... rmAction = QtGui.QAction(QtGui.QIcon('icons/close-x.png'), 'Delete', self) rmAction.triggered.connect(self._removeMarker) rmAllAction = QtGui.QAction(QtGui.QIcon('icons/clear-all.png'), 'Remove all', self) rmAllAction.triggered.connect(self.purgeMarker) self.gotoAction = QtGui.QAction(QtGui.QIcon('icons/go-next.png'), 'Goto', self) self.gotoAction.triggered.connect(self._gotoFromMarker) #menus self.ui_List.customContextMenuRequested.connect(self._openListMenu) self._listMenu = QMenu() self._listMenu.addAction(self.gotoAction) self._listMenu.addSeparator() self._listMenu.addAction(rmAction) self._listMenu.addAction(rmAllAction) def __resetDial(self): self.ui_Dial.setProperty("value", 0) def __gotoFrame(self): self.__videoController._gotoFrame(self.ui_GotoField.value()) def __createProgressBar(self): self.progressBar = QtGui.QProgressBar(self) #self.progressBar.setRange(0,1) #self.progressBar.setValue(-1) #self.progressBar.reset() self.progressBar.setMinimum(0) self.progressBar.setMaximumWidth(150) self.progressBar.setVisible(False) return self.progressBar def showBusy(self): self.progressBar.setRange(0,0) self.progressBar.setVisible(True) def stopProgress(self): self.progressBar.setVisible(False) def __createListWidget(self): iconList=QListWidget() iconList.setAlternatingRowColors(True) iconList.setContextMenuPolicy(Qt.CustomContextMenu) #iconList.setStyleSheet("QListWidget { background: red; } QListWidget::item { background: yellow; } QListWidget::item:selected { background: blue; }") iconList.setStyleSheet("QListWidget::item:selected:active { background: #28D9FF; color:#FFFFFF; } ")#that text color seems not to work! return iconList #---List widget context menu def _removeMarker(self,whatis): selectionList = self.ui_List.selectedIndexes() if len(selectionList)==0: return item = selectionList[0] self.ui_List.takeItem(item.row()) self.__videoController.removeVideoCutIndex(item.row()) def clearMarkerList(self): self.ui_List.clear() #remove contents, remove file def purgeMarker(self): self.ui_List.clear() self.__videoController.purgeVideoCuts() def _gotoFromMarker(self,whatis): selectionList = self.ui_List.selectedIndexes() if len(selectionList)==0: return item = selectionList[0] self.__videoController.gotoCutIndex(item.row()) def _openListMenu(self,position): selectionList = self.ui_List.selectedIndexes() if len(selectionList)==0: return self._listMenu.exec_(self.ui_List.viewport().mapToGlobal(position))
class DeviceTable(QtGui.QDialog): def __init__(self, parent=None): super(DeviceTable, self).__init__(parent) self.logger = logging.getLogger('console') self.records = {} self.resize(800,800) self.label_a = QLabel() self.label_a.setText("A: ") self.spinbox_a = QSpinBox() self.spinbox_a.setRange(-103,-38) self.spinbox_a.setValue(-41) self.spinbox_a.valueChanged.connect(self.spinboxChanged) self.spinboxChanged(self.spinbox_a.value()) self.slider_label = QLabel() self.slider_label.setText(" n:") self.slider = QSlider() self.slider.setOrientation(QtCore.Qt.Horizontal) self.slider.setRange(0, 40) self.slider.setValue(22) self.slider.setTickInterval(1) self.slider.valueChanged.connect(self.sliderChanged) self.sliderChanged(self.slider.value()) self.button_reset = QPushButton() self.button_reset.setText("Reset") self.button_reset.clicked.connect(self.reset) self.horizontalLayout = QtGui.QHBoxLayout() self.horizontalLayout.addWidget(self.label_a) self.horizontalLayout.addWidget(self.spinbox_a) self.horizontalLayout.addWidget(self.slider_label) self.horizontalLayout.addWidget(self.slider) self.horizontalLayout.addWidget(self.button_reset) self.table = QTableWidget() self.table.setColumnCount(8) self.table.setHorizontalHeaderLabels(['MAC','Count','RSSI','RSSI','Distance','RSSI_F', 'DIST_F','Battery']) # matplotlib stuff self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.buttonPlot = QPushButton('Plot') self.buttonPlot.clicked.connect(self.plot) self.plot_layout = QtGui.QVBoxLayout() self.plot_layout.addWidget(self.toolbar) self.plot_layout.addWidget(self.canvas) self.plot_layout.addWidget(self.buttonPlot) self.ax = self.figure.add_subplot(111) self.ax.hold(False) self.statusbar = QStatusBar() self.statusbar.showMessage('Opening log file') self.verticalLayout = QtGui.QVBoxLayout() self.verticalLayout.addWidget(self.table) self.verticalLayout.addLayout(self.plot_layout) self.verticalLayout.addWidget(self.statusbar) self.mainLayout = QtGui.QVBoxLayout(self) self.mainLayout.addLayout(self.horizontalLayout) self.mainLayout.addLayout(self.verticalLayout) self.is_recording = False self.data = [-50] self.data_f = [-50] self.data_y = [0] def showEvent(self, *args, **kwargs): self.parent().ble_ascii_received.connect(self.bleAsciiReceived) return QtGui.QDialog.showEvent(self, *args, **kwargs) def closeEvent(self, *args, **kwargs): self.reset() self.parent().ble_ascii_received.disconnect(self.bleAsciiReceived) return QtGui.QDialog.closeEvent(self, *args, **kwargs) def spinboxChanged(self, value): self.distance_A = value; def sliderChanged(self, value): self.distance_n = value / 10.0; self.slider_label.setText("n [{0:.1f}]: ".format(self.distance_n)) def reset(self): self.table.setRowCount(0) self.records = {} try: self.file.close() except Exception: pass self.file = None self.is_recording = False def plot(self): data = [random.random() for i in range(10)] self.ax = self.figure.add_subplot(111) self.ax.hold(False) self.ax.plot(data, '*-') self.canvas.draw() def startRecording(self): self.filename = "./logs/" + time.strftime("%y%m%d-%H%M%S") + ".txt" try: if not os.path.exists("./logs"): os.makedirs("./logs") self.file = open(self.filename, "w") self.statusbar.showMessage("Recording to " + self.filename) self.is_recording = True except Exception, e: self.file = None self.is_recording = True self.logger.error(e) self.statusbar.showMessage("NOT RECORDING - check console log for errors")