Beispiel #1
0
 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
Beispiel #2
0
 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
Beispiel #3
0
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
Beispiel #4
0
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)
Beispiel #5
0
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")